diff --git a/.env b/.env new file mode 100644 index 00000000..7e5aec61 --- /dev/null +++ b/.env @@ -0,0 +1,6 @@ +# SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +# +# SPDX-License-Identifier: AGPL-3.0-only + +# Infer targets automatically? https://nx.dev/concepts/inferred-tasks +NX_ADD_PLUGINS=false diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000..f9494937 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,5 @@ +# SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +# +# SPDX-License-Identifier: AGPL-3.0-only + +node_modules diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 00000000..b3a07645 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,75 @@ +{ + "root": true, + "extends": "@jvalue/eslint-config-jvalue", + "ignorePatterns": ["**/*"], + "plugins": ["@nx"], + "overrides": [ + { + "files": ["*.ts", ".tsx"], + "rules": { + "import/no-unresolved": "off" + } + }, + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": { + "@nx/enforce-module-boundaries": [ + "error", + { + "enforceBuildableLibDependency": true, + "allow": [], + "depConstraints": [ + { + "sourceTag": "*", + "onlyDependOnLibsWithTags": ["*"] + } + ] + } + ], + "@typescript-eslint/consistent-type-imports": [ + "warn", + { + "fixStyle": "inline-type-imports" + } + ] + } + }, + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "plugins": ["unicorn"], + "rules": { + "unicorn/prefer-node-protocol": "warn", + "unicorn/import-style": "warn", + "no-restricted-imports": [ + "error", + { + "paths": [ + { + "name": "node:assert", + "message": "Please use the library `assert` instead to keep browser compatibility. You might need to disable rule `unicorn/prefer-node-protocol` to do so." + } + ] + } + ] + } + }, + { + "files": ["*.ts", "*.tsx"], + "extends": ["plugin:@nx/typescript"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "extends": ["plugin:@nx/javascript"], + "rules": {} + }, + { + "files": ["*.spec.ts", "*.spec.tsx", "*.spec.js", "*.spec.jsx"], + "plugins": ["vitest"], + "rules": { + // you should turn the original rule off *only* for test files + "@typescript-eslint/unbound-method": "off" + } + } + ] +} diff --git a/.eslintrc.json.license b/.eslintrc.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/.eslintrc.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..71920095 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +# SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +# +# SPDX-License-Identifier: AGPL-3.0-only + +* text=auto eol=lf diff --git a/.github/ISSUE_TEMPLATE/bug.md b/.github/ISSUE_TEMPLATE/bug.md new file mode 100644 index 00000000..dd42de6f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug.md @@ -0,0 +1,15 @@ +--- +name: Bug +about: Description of the bug +title: '[BUG] ' +labels: bug +assignees: '' + +--- + +## Steps to reproduce +1. + +## Description +- Expected: +- Actual: diff --git a/.github/ISSUE_TEMPLATE/bug.md.license b/.github/ISSUE_TEMPLATE/bug.md.license new file mode 100644 index 00000000..8d58921f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: CC-BY-4.0 diff --git a/.github/ISSUE_TEMPLATE/feature.md b/.github/ISSUE_TEMPLATE/feature.md new file mode 100644 index 00000000..71f56396 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature.md @@ -0,0 +1,30 @@ +--- +name: Feature +about: Use this template for new features. +title: "[FEATURE] " +labels: enhancement, needs triage +assignees: '' +--- + +<!-- +Note: Please search to see if a similar issue already exists. +--> + +## User Story +1. As a {user/persona} +2. I want {to perform this action} +3. So that {I can accomplish this goal} + +## User Acceptance Criteria +- [ ] {acceptance criteria 1} + +## Examples +<!-- example if present --> + +## Notes +<!-- further notes if present --> + +## Definitions of Done +- [ ] A PR has been opened and accepted +- [ ] All user acceptance criteria are met +- [ ] All tests are passing diff --git a/.github/ISSUE_TEMPLATE/feature.md.license b/.github/ISSUE_TEMPLATE/feature.md.license new file mode 100644 index 00000000..8d58921f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: CC-BY-4.0 diff --git a/.github/ISSUE_TEMPLATE/rfc.md b/.github/ISSUE_TEMPLATE/rfc.md new file mode 100644 index 00000000..ed60c4cc --- /dev/null +++ b/.github/ISSUE_TEMPLATE/rfc.md @@ -0,0 +1,22 @@ +--- +name: RFC +about: Use this template to track RFCs that should be created. +title: "[RFC] <title>" +labels: rfc +assignees: '' +--- + +<!-- +Note: Please search to see if a similar rfc already exists. +--> + +## Short Description +<!-- Short description, details follow in the RFC --> + +## Definition of Done +- [ ] The RFC document is created following the existing pattern +- [ ] A pull request with the RFC document is opened +- [ ] The pull request passed peer review and is accepted +- [ ] An issue is opened that tracks the implementation of the RFC + +**Note:** The details of the RFC will be discussed in the PR, not in this issue! diff --git a/.github/ISSUE_TEMPLATE/rfc.md.license b/.github/ISSUE_TEMPLATE/rfc.md.license new file mode 100644 index 00000000..8d58921f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/rfc.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: CC-BY-4.0 diff --git a/.github/workflows/check-paths-for-windows.yaml b/.github/workflows/check-paths-for-windows.yaml new file mode 100644 index 00000000..0562f35b --- /dev/null +++ b/.github/workflows/check-paths-for-windows.yaml @@ -0,0 +1,29 @@ +# SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +# +# SPDX-License-Identifier: AGPL-3.0-only + +name: Check paths for windows + +on: + push: + branches: + - main + pull_request: + workflow_dispatch: + +jobs: + check-paths-for-windows: + name: Check paths for Windows + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - uses: actions/setup-node@v3 + with: + node-version: 'lts/*' + + - name: Check paths for Windows + run: node ./tools/scripts/check-for-invalid-windows-paths.mjs diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 00000000..d34da5e7 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,54 @@ +# SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +# +# SPDX-License-Identifier: AGPL-3.0-only + +name: CI + +on: + push: + branches: + - main + pull_request: + workflow_dispatch: + +jobs: + ci: + name: CI + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: REUSE Compliance Check + uses: fsfe/reuse-action@v1 + + - uses: actions/setup-node@v3 + with: + node-version: 'lts/*' + + - name: Install dependencies + run: npm ci + + - uses: nrwl/nx-set-shas@v3 + + - name: Langium generate + run: npm run generate + + - name: Build + run: npx nx affected --target=build --configuration=prod --parallel=3 + + - name: Lint + run: npx nx affected --target=lint --parallel=3 --maxWarnings=0 + + - name: Test + run: npx nx affected --target=test --parallel=3 --base=remotes/origin/main --head=HEAD --ci + + # Ensure this works for future release publishing + - name: Publish Dry-Run + run: npx nx affected --target=pre-publish --parallel=3 --base=remotes/origin/main --head=HEAD --ci + + # Ensure this works for future release publishing + - name: Pack VSCode Extension + run: npx nx run vs-code-extension:pack:prod diff --git a/.github/workflows/cla.yaml b/.github/workflows/cla.yaml new file mode 100644 index 00000000..dbbe686e --- /dev/null +++ b/.github/workflows/cla.yaml @@ -0,0 +1,41 @@ +# SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +# +# SPDX-License-Identifier: AGPL-3.0-only + +name: 'CLA Assistant' +on: + issue_comment: + types: [created] + pull_request_target: + types: [opened, closed, synchronize] + +jobs: + CLAAssistant: + if: github.event.repository.full_name == 'jvalue/jayvee' + runs-on: ubuntu-latest + steps: + - name: 'CLA Assistant' + if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target' + # Beta Release + uses: contributor-assistant/github-action@v2.2.0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # the below token should have repo scope and must be manually added by you in the repository's secret + PERSONAL_ACCESS_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }} + with: + path-to-signatures: 'signatures/version1/cla.json' + path-to-document: 'https://gist.github.com/profriehle/44d5ed0f2fbaac043eef038c8ba5cbab' # e.g. a CLA or a DCO document + # branch should not be protected + branch: 'main' + allowlist: dependabot[bot] + + # the followings are the optional inputs - If the optional inputs are not given, then default values will be taken + remote-organization-name: 'jvalue' # enter the remote organization name where the signatures should be stored (Default is storing the signatures in the same repository) + remote-repository-name: 'cla' # enter the remote repository name where the signatures should be stored (Default is storing the signatures in the same repository) + #create-file-commit-message: 'For example: Creating file for storing CLA Signatures' + #signed-commit-message: 'For example: $contributorName has signed the CLA in #$pullRequestNo' + #custom-notsigned-prcomment: 'pull request comment with Introductory message to ask new contributors to sign' + #custom-pr-sign-comment: 'The signature to be committed in order to sign the CLA' + #custom-allsigned-prcomment: 'pull request comment when all contributors has signed, defaults to **CLA Assistant Lite bot** All Contributors have signed the CLA.' + #lock-pullrequest-aftermerge: false - if you don't want this bot to automatically lock the pull request after merging (default - true) + #use-dco-flag: true - If you are using DCO instead of CLA diff --git a/.github/workflows/gh-pages.yaml b/.github/workflows/gh-pages.yaml new file mode 100644 index 00000000..5eb1779e --- /dev/null +++ b/.github/workflows/gh-pages.yaml @@ -0,0 +1,60 @@ +# SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +# +# SPDX-License-Identifier: AGPL-3.0-only + +name: Deploy Docs to GitHub Pages + +on: + push: + branches: + - main + workflow_dispatch: + +permissions: + contents: read + actions: read # for nrwl/nx-set-shas@v3 + pages: write + id-token: write + +# Avoid concurrent deployments +concurrency: + group: 'pages' + cancel-in-progress: true + +jobs: + deploy: + name: Deploy docs + runs-on: ubuntu-latest + + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - uses: actions/setup-node@v3 + with: + node-version: 'lts/*' + + - name: Install dependencies + run: npm ci + + - uses: nrwl/nx-set-shas@v3 + + - name: Build docs + run: npx nx build docs + + - name: Setup GitHub Pages + uses: actions/configure-pages@v2 + + - name: Upload artifact to GitHub Pages + uses: actions/upload-pages-artifact@v1 + with: + path: './dist/apps/docs' + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v1 \ No newline at end of file diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml new file mode 100644 index 00000000..965b4b48 --- /dev/null +++ b/.github/workflows/publish.yaml @@ -0,0 +1,60 @@ +# SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +# +# SPDX-License-Identifier: AGPL-3.0-only + +name: Publish Packages + +on: + release: + types: [created] + +# https://docs.github.com/en/actions/guides/publishing-nodejs-packages +jobs: + publish: + runs-on: ubuntu-latest + permissions: + contents: write + packages: write + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + with: + node-version: 'lts/*' + always-auth: true + registry-url: 'https://registry.npmjs.org' + - run: npm ci + - name: Publish interpreter-lib + run: npx nx run interpreter-lib:publish:prod + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + - name: Publish language server + run: npx nx run language-server:publish:prod + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + - name: Publish interpreter + run: npx nx run interpreter:publish:prod + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + - name: Publish language server web worker + run: npx nx run language-server-web-worker:publish:prod + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + - name: Publish monaco editor + run: npx nx run monaco-editor:publish:prod + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + - name: Pack VS Code extension + run: npx nx run vs-code-extension:pack:prod + - id: get_release + uses: bruceadams/get-release@v1.3.2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Upload packed VS Code extension as release artifact + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.get_release.outputs.upload_url }} + asset_path: ./dist/apps/vs-code-extension/jayvee.vsix + asset_name: jayvee.vsix + asset_content_type: application/octet-stream diff --git a/.gitignore b/.gitignore index a491c36f..4f4d3c6c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,60 @@ -node_modules/ -*.js +# SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +# +# SPDX-License-Identifier: AGPL-3.0-only + +# See http://help.github.com/ignore-files/ for more about ignoring files. + +# compiled output +dist +tmp +/out-tsc + +# files generated from jayvee runs +*.db +*.sql *.sqlite + +# dependencies +node_modules + +# IDEs and editors +/.idea +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# IDE - VSCode +.vscode/* +!.vscode/settings.json +!.vscode/settings.json.license +!.vscode/tasks.json +!.vscode/tasks.json.license +!.vscode/launch.json +!.vscode/launch.json.license +!.vscode/extensions.json +!.vscode/extensions.json.license +# VSCode - local history extension +.history/* + +# misc +/.sass-cache +/connect.lock +/coverage +/libpeerconnection.log +npm-debug.log +yarn-error.log +testem.log +/typings + +# System Files +.DS_Store +Thumbs.db + +# Generated Docusaurus files +.docusaurus/ +.cache-loader/ + +.nx/cache diff --git a/.npmrc b/.npmrc new file mode 100644 index 00000000..d0e41d33 --- /dev/null +++ b/.npmrc @@ -0,0 +1,5 @@ +# SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +# +# SPDX-License-Identifier: AGPL-3.0-only + +legacy-peer-deps=true # Docusaurus is still on React 17, thus we need this option. Remove once docusaurus migrates to React 18. \ No newline at end of file diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..940d1722 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,17 @@ +# SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +# +# SPDX-License-Identifier: AGPL-3.0-only + +# Add files here to ignore them from prettier formatting + +/dist +/coverage + +/libs/language-server/syntaxes/*.json +/libs/language-server/syntaxes/*.ts +/libs/language-server/src/lib/ast/generated/*.ts + +.prettierignore +.docusaurus/ + +/.nx/cache \ No newline at end of file diff --git a/.prettierrc.js b/.prettierrc.js new file mode 100644 index 00000000..d860d99d --- /dev/null +++ b/.prettierrc.js @@ -0,0 +1,5 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +module.exports = require('@jvalue/eslint-config-jvalue/.prettierrc.js'); diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000..e656551d --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,8 @@ +{ + "recommendations": [ + "nrwl.angular-console", + "langium.langium-vscode", + "esbenp.prettier-vscode", + "vitest.explorer" + ] +} diff --git a/.vscode/extensions.json.license b/.vscode/extensions.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/.vscode/extensions.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..f54ba5b9 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,56 @@ +// A launch configuration that launches the extension inside a new window +// Use IntelliSense to learn about possible attributes. +// Hover to view descriptions of existing attributes. +// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Run Extension", + "type": "extensionHost", + "request": "launch", + "preLaunchTask": "Build VS Code Extension", + "args": [ + "${workspaceFolder}/example", + "--extensionDevelopmentPath=${workspaceFolder}/dist/apps/vs-code-extension" + ], + "sourceMaps": true, + "outFiles": [ + "${workspaceFolder}/dist/apps/vs-code-extension/extension.cjs" + ] + }, + { + "name": "Attach to Language Server", + "type": "node", + "port": 6009, + "request": "attach", + "skipFiles": ["<node_internals>/**"], + "sourceMaps": true, + "restart": true, + "timeout": 60000, + "continueOnAttach": true, + "outFiles": [ + "${workspaceFolder}/dist/apps/vs-code-extension/language-server.cjs", + "${workspaceFolder}/node_modules/langium" + ] + }, + { + "name": "Run Cars Example", + "command": "npm run example:cars", + "request": "launch", + "type": "node-terminal" + }, + { + "name": "Run Gas Reserve Example", + "command": "npm run example:gas", + "request": "launch", + "type": "node-terminal" + }, + { + "name": "Run GTFS Example", + "command": "npm run example:gtfs", + "request": "launch", + "type": "node-terminal" + } + ] +} diff --git a/.vscode/launch.json.license b/.vscode/launch.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/.vscode/launch.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 00000000..0e9a3b53 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,12 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "Build VS Code Extension", + "type": "shell", + "command": "npx nx build vs-code-extension" + } + ] +} diff --git a/.vscode/tasks.json.license b/.vscode/tasks.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/.vscode/tasks.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..d4022c02 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,134 @@ +<!-- +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg, 2014 Coraline Ada Ehmke + +SPDX-License-Identifier: CC-BY-4.0 +--> + +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +community@jvalue.org. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..e33e7178 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,78 @@ +<!-- +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only +--> + +# Jayvee Contributing Guide + +Welcome to the contributing guidelines for Jayvee! Thank you for considering to contribute to our open source project. +We appreciate your time and effort in helping us improve our project. + + +## Code of Conduct + +Before we begin, please take a moment to review our [Code of Conduct](CODE_OF_CONDUCT.md). +It outlines the expectations of behavior and respect that we expect from all contributors to our project. + + +## Ways to Contribute + +There are several ways in which you can contribute to Jayvee: +- Reporting issues or suggesting new features +- Designing an RFC +- Writing code +- Reviewing code +- Creating and updating documentation + + +### Reporting Issues or Suggesting New Features + +If you find a bug or have a suggestion for a new feature, please submit an [issue](https://github.com/jvalue/jayvee/issues) on our GitHub repository. +Please make sure to include as much information as possible, such as steps to reproduce the issue, error messages, and screenshots. +We recommend to use one of the existing issue templates to ensure consistent form of the issues. +We appreciate any input you have to offer. + + +### Designing an RFC + +We use an RFC process to design new language features. Please read about our current RFC process in the documentation: [Language Design Process (RFCs)](https://jvalue.github.io/jayvee/docs/dev/rfc-process). You can find previous RFCs [here](/rfc/). + + +### Writing Code + +If you're interested in writing code for Jayvee, please follow these steps: +1. Fork our GitHub repository to your own account. +2. Clone the forked repository to your local machine. +3. Create a new branch for your changes. Please name your branch something descriptive, such as "feature/my-new-feature" or "bugfix/my-bug-fix". If your contribution addresses an existing issue, please use the branch format "XXX-my-new-feature" where XXX is the issue id. +4. Make the changes you want to make on your branch. +5. Test your changes thoroughly to make sure they work as expected. +6. If you're planning on making a significant contribution, you will need to sign a Contributor License Agreement (CLA). This is a legal document that ensures that the project and its users are protected, and that the contributions you make are licensed appropriately. You can find the [CLA](https://oss.cs.fau.de/teaching/course-resources/contributor-agreement/) here. Please sign it and send us a scan to [jvalue@group.riehle.org]. The CLA Assistant GitHub Action acts as second redundant mechanism. +7. Commit your changes and push your branch to your forked repository. +8. Submit a pull request to our main repository. +9. Self-review your pull request. If you have open questions, please add comments to the code accordingly. + +Please make sure to include a detailed description of your changes in your pull request and document additions and change on the language in the [developer docs](/apps/docs//docs/dev/). +We appreciate comments explaining why you made specific changes, and any context that may be useful for reviewers. + + +### Reviewing Code + +We appreciate any help with reviewing code. +If you have some time to spare, please take a look at our open pull requests and provide your feedback. +You can leave comments directly on the pull request or on the code itself. + + +### Creating and Updating Documentation + +If you have experience with technical writing, we would appreciate any help with creating or updating our documentation. +Please submit a pull request with your changes to the `apps/docs` project. + + +## Conclusion + +Thank you for taking the time to read our contributing guidelines. +We hope that you find them helpful. +If you have any questions, please don't hesitate to contact us through our [GitHub repository](https://github.com/jvalue/jayvee). +We look forward to your contributions! + diff --git a/LICENSES/AGPL-3.0-only.txt b/LICENSES/AGPL-3.0-only.txt new file mode 100644 index 00000000..0c97efd2 --- /dev/null +++ b/LICENSES/AGPL-3.0-only.txt @@ -0,0 +1,235 @@ +GNU AFFERO GENERAL PUBLIC LICENSE +Version 3, 19 November 2007 + +Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + +Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. + + Preamble + +The GNU Affero General Public License is a free, copyleft license for software and other kinds of works, specifically designed to ensure cooperation with the community in the case of network server software. + +The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, our General Public Licenses are intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. + +When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. + +Developers that use our General Public Licenses protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License which gives you legal permission to copy, distribute and/or modify the software. + +A secondary benefit of defending all users' freedom is that improvements made in alternate versions of the program, if they receive widespread use, become available for other developers to incorporate. Many developers of free software are heartened and encouraged by the resulting cooperation. However, in the case of software used on network servers, this result may fail to come about. The GNU General Public License permits making a modified version and letting the public access it on a server without ever releasing its source code to the public. + +The GNU Affero General Public License is designed specifically to ensure that, in such cases, the modified source code becomes available to the community. It requires the operator of a network server to provide the source code of the modified version running there to the users of that server. Therefore, public use of a modified version, on a publicly accessible server, gives the public access to the source code of the modified version. + +An older license, called the Affero General Public License and published by Affero, was designed to accomplish similar goals. This is a different license, not a version of the Affero GPL, but Affero has released a new version of the Affero GPL which permits relicensing under this license. + +The precise terms and conditions for copying, distribution and modification follow. + + TERMS AND CONDITIONS + +0. Definitions. + +"This License" refers to version 3 of the GNU Affero General Public License. + +"Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. + +"The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. + +To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. + +A "covered work" means either the unmodified Program or a work based on the Program. + +To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. + +To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. + +An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. + +1. Source Code. +The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. + +A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. + +The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. + +The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those +subprograms and other parts of the work. + +The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. + +The Corresponding Source for a work in source code form is that same work. + +2. Basic Permissions. +All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. + +You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. + +Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. + +3. Protecting Users' Legal Rights From Anti-Circumvention Law. +No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. + +When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. + +4. Conveying Verbatim Copies. +You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. + +You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. + +5. Conveying Modified Source Versions. +You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". + + c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. + +A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. + +6. Conveying Non-Source Forms. +You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: + + a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. + + d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. + +A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. + +A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. + +"Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. + +If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). + +The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. + +Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. + +7. Additional Terms. +"Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. + +Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or authors of the material; or + + e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. + +All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. + +If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. + +8. Termination. + +You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). + +However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. + +Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. + +Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. + +9. Acceptance Not Required for Having Copies. + +You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. + +10. Automatic Licensing of Downstream Recipients. + +Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. + +An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. + +You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. + +11. Patents. + +A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". + +A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. + +In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. + +If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. + +A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. + +12. No Surrender of Others' Freedom. + +If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. + +13. Remote Network Interaction; Use with the GNU General Public License. + +Notwithstanding any other provision of this License, if you modify the Program, your modified version must prominently offer all users interacting with it remotely through a computer network (if your version supports such interaction) an opportunity to receive the Corresponding Source of your version by providing access to the Corresponding Source from a network server at no charge, through some standard or customary means of facilitating copying of software. This Corresponding Source shall include the Corresponding Source for any work covered by version 3 of the GNU General Public License that is incorporated pursuant to the following paragraph. + +Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the work with which it is combined will remain governed by version 3 of the GNU General Public License. + +14. Revised Versions of this License. + +The Free Software Foundation may publish revised and/or new versions of the GNU Affero General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU Affero General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU Affero General Public License, you may choose any version ever published by the Free Software Foundation. + +If the Program specifies that a proxy can decide which future versions of the GNU Affero General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. + +Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. + +15. Disclaimer of Warranty. + +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +16. Limitation of Liability. + +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +17. Interpretation of Sections 15 and 16. + +If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. + +END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + +If your software can interact with users remotely through a computer network, you should also make sure that it provides a way for users to get its source. For example, if your program is a web application, its interface could display a "Source" link that leads users to an archive of the code. There are many ways you could offer source, and different solutions will be better for different programs; see section 13 for the specific requirements. + +You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU AGPL, see <http://www.gnu.org/licenses/>. diff --git a/LICENSES/Apache-2.0.txt b/LICENSES/Apache-2.0.txt new file mode 100644 index 00000000..9d72f1cc --- /dev/null +++ b/LICENSES/Apache-2.0.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. \ No newline at end of file diff --git a/LICENSES/CC-BY-4.0.txt b/LICENSES/CC-BY-4.0.txt new file mode 100644 index 00000000..4ea99c21 --- /dev/null +++ b/LICENSES/CC-BY-4.0.txt @@ -0,0 +1,395 @@ +Attribution 4.0 International + +======================================================================= + +Creative Commons Corporation ("Creative Commons") is not a law firm and +does not provide legal services or legal advice. Distribution of +Creative Commons public licenses does not create a lawyer-client or +other relationship. Creative Commons makes its licenses and related +information available on an "as-is" basis. Creative Commons gives no +warranties regarding its licenses, any material licensed under their +terms and conditions, or any related information. Creative Commons +disclaims all liability for damages resulting from their use to the +fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and +conditions that creators and other rights holders may use to share +original works of authorship and other material subject to copyright +and certain other rights specified in the public license below. The +following considerations are for informational purposes only, are not +exhaustive, and do not form part of our licenses. + + Considerations for licensors: Our public licenses are + intended for use by those authorized to give the public + permission to use material in ways otherwise restricted by + copyright and certain other rights. Our licenses are + irrevocable. Licensors should read and understand the terms + and conditions of the license they choose before applying it. + Licensors should also secure all rights necessary before + applying our licenses so that the public can reuse the + material as expected. Licensors should clearly mark any + material not subject to the license. This includes other CC- + licensed material, or material used under an exception or + limitation to copyright. More considerations for licensors: + wiki.creativecommons.org/Considerations_for_licensors + + Considerations for the public: By using one of our public + licenses, a licensor grants the public permission to use the + licensed material under specified terms and conditions. If + the licensor's permission is not necessary for any reason--for + example, because of any applicable exception or limitation to + copyright--then that use is not regulated by the license. Our + licenses grant only permissions under copyright and certain + other rights that a licensor has authority to grant. Use of + the licensed material may still be restricted for other + reasons, including because others have copyright or other + rights in the material. A licensor may make special requests, + such as asking that all changes be marked or described. + Although not required by our licenses, you are encouraged to + respect those requests where reasonable. More considerations + for the public: + wiki.creativecommons.org/Considerations_for_licensees + +======================================================================= + +Creative Commons Attribution 4.0 International Public License + +By exercising the Licensed Rights (defined below), You accept and agree +to be bound by the terms and conditions of this Creative Commons +Attribution 4.0 International Public License ("Public License"). To the +extent this Public License may be interpreted as a contract, You are +granted the Licensed Rights in consideration of Your acceptance of +these terms and conditions, and the Licensor grants You such rights in +consideration of benefits the Licensor receives from making the +Licensed Material available under these terms and conditions. + + +Section 1 -- Definitions. + + a. Adapted Material means material subject to Copyright and Similar + Rights that is derived from or based upon the Licensed Material + and in which the Licensed Material is translated, altered, + arranged, transformed, or otherwise modified in a manner requiring + permission under the Copyright and Similar Rights held by the + Licensor. For purposes of this Public License, where the Licensed + Material is a musical work, performance, or sound recording, + Adapted Material is always produced where the Licensed Material is + synched in timed relation with a moving image. + + b. Adapter's License means the license You apply to Your Copyright + and Similar Rights in Your contributions to Adapted Material in + accordance with the terms and conditions of this Public License. + + c. Copyright and Similar Rights means copyright and/or similar rights + closely related to copyright including, without limitation, + performance, broadcast, sound recording, and Sui Generis Database + Rights, without regard to how the rights are labeled or + categorized. For purposes of this Public License, the rights + specified in Section 2(b)(1)-(2) are not Copyright and Similar + Rights. + + d. Effective Technological Measures means those measures that, in the + absence of proper authority, may not be circumvented under laws + fulfilling obligations under Article 11 of the WIPO Copyright + Treaty adopted on December 20, 1996, and/or similar international + agreements. + + e. Exceptions and Limitations means fair use, fair dealing, and/or + any other exception or limitation to Copyright and Similar Rights + that applies to Your use of the Licensed Material. + + f. Licensed Material means the artistic or literary work, database, + or other material to which the Licensor applied this Public + License. + + g. Licensed Rights means the rights granted to You subject to the + terms and conditions of this Public License, which are limited to + all Copyright and Similar Rights that apply to Your use of the + Licensed Material and that the Licensor has authority to license. + + h. Licensor means the individual(s) or entity(ies) granting rights + under this Public License. + + i. Share means to provide material to the public by any means or + process that requires permission under the Licensed Rights, such + as reproduction, public display, public performance, distribution, + dissemination, communication, or importation, and to make material + available to the public including in ways that members of the + public may access the material from a place and at a time + individually chosen by them. + + j. Sui Generis Database Rights means rights other than copyright + resulting from Directive 96/9/EC of the European Parliament and of + the Council of 11 March 1996 on the legal protection of databases, + as amended and/or succeeded, as well as other essentially + equivalent rights anywhere in the world. + + k. You means the individual or entity exercising the Licensed Rights + under this Public License. Your has a corresponding meaning. + + +Section 2 -- Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, + the Licensor hereby grants You a worldwide, royalty-free, + non-sublicensable, non-exclusive, irrevocable license to + exercise the Licensed Rights in the Licensed Material to: + + a. reproduce and Share the Licensed Material, in whole or + in part; and + + b. produce, reproduce, and Share Adapted Material. + + 2. Exceptions and Limitations. For the avoidance of doubt, where + Exceptions and Limitations apply to Your use, this Public + License does not apply, and You do not need to comply with + its terms and conditions. + + 3. Term. The term of this Public License is specified in Section + 6(a). + + 4. Media and formats; technical modifications allowed. The + Licensor authorizes You to exercise the Licensed Rights in + all media and formats whether now known or hereafter created, + and to make technical modifications necessary to do so. The + Licensor waives and/or agrees not to assert any right or + authority to forbid You from making technical modifications + necessary to exercise the Licensed Rights, including + technical modifications necessary to circumvent Effective + Technological Measures. For purposes of this Public License, + simply making modifications authorized by this Section 2(a) + (4) never produces Adapted Material. + + 5. Downstream recipients. + + a. Offer from the Licensor -- Licensed Material. Every + recipient of the Licensed Material automatically + receives an offer from the Licensor to exercise the + Licensed Rights under the terms and conditions of this + Public License. + + b. No downstream restrictions. You may not offer or impose + any additional or different terms or conditions on, or + apply any Effective Technological Measures to, the + Licensed Material if doing so restricts exercise of the + Licensed Rights by any recipient of the Licensed + Material. + + 6. No endorsement. Nothing in this Public License constitutes or + may be construed as permission to assert or imply that You + are, or that Your use of the Licensed Material is, connected + with, or sponsored, endorsed, or granted official status by, + the Licensor or others designated to receive attribution as + provided in Section 3(a)(1)(A)(i). + + b. Other rights. + + 1. Moral rights, such as the right of integrity, are not + licensed under this Public License, nor are publicity, + privacy, and/or other similar personality rights; however, to + the extent possible, the Licensor waives and/or agrees not to + assert any such rights held by the Licensor to the limited + extent necessary to allow You to exercise the Licensed + Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this + Public License. + + 3. To the extent possible, the Licensor waives any right to + collect royalties from You for the exercise of the Licensed + Rights, whether directly or through a collecting society + under any voluntary or waivable statutory or compulsory + licensing scheme. In all other cases the Licensor expressly + reserves any right to collect such royalties. + + +Section 3 -- License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the +following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material (including in modified + form), You must: + + a. retain the following if it is supplied by the Licensor + with the Licensed Material: + + i. identification of the creator(s) of the Licensed + Material and any others designated to receive + attribution, in any reasonable manner requested by + the Licensor (including by pseudonym if + designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of + warranties; + + v. a URI or hyperlink to the Licensed Material to the + extent reasonably practicable; + + b. indicate if You modified the Licensed Material and + retain an indication of any previous modifications; and + + c. indicate the Licensed Material is licensed under this + Public License, and include the text of, or the URI or + hyperlink to, this Public License. + + 2. You may satisfy the conditions in Section 3(a)(1) in any + reasonable manner based on the medium, means, and context in + which You Share the Licensed Material. For example, it may be + reasonable to satisfy the conditions by providing a URI or + hyperlink to a resource that includes the required + information. + + 3. If requested by the Licensor, You must remove any of the + information required by Section 3(a)(1)(A) to the extent + reasonably practicable. + + 4. If You Share Adapted Material You produce, the Adapter's + License You apply must not prevent recipients of the Adapted + Material from complying with this Public License. + + +Section 4 -- Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that +apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right + to extract, reuse, reproduce, and Share all or a substantial + portion of the contents of the database; + + b. if You include all or a substantial portion of the database + contents in a database in which You have Sui Generis Database + Rights, then the database in which You have Sui Generis Database + Rights (but not its individual contents) is Adapted Material; and + + c. You must comply with the conditions in Section 3(a) if You Share + all or a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not +replace Your obligations under this Public License where the Licensed +Rights include other Copyright and Similar Rights. + + +Section 5 -- Disclaimer of Warranties and Limitation of Liability. + + a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE + EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS + AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF + ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, + IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, + WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, + ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT + KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT + ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. + + b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE + TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, + NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, + INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, + COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR + USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR + DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR + IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. + + c. The disclaimer of warranties and limitation of liability provided + above shall be interpreted in a manner that, to the extent + possible, most closely approximates an absolute disclaimer and + waiver of all liability. + + +Section 6 -- Term and Termination. + + a. This Public License applies for the term of the Copyright and + Similar Rights licensed here. However, if You fail to comply with + this Public License, then Your rights under this Public License + terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under + Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided + it is cured within 30 days of Your discovery of the + violation; or + + 2. upon express reinstatement by the Licensor. + + For the avoidance of doubt, this Section 6(b) does not affect any + right the Licensor may have to seek remedies for Your violations + of this Public License. + + c. For the avoidance of doubt, the Licensor may also offer the + Licensed Material under separate terms or conditions or stop + distributing the Licensed Material at any time; however, doing so + will not terminate this Public License. + + d. Sections 1, 5, 6, 7, and 8 survive termination of this Public + License. + + +Section 7 -- Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different + terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the + Licensed Material not stated herein are separate from and + independent of the terms and conditions of this Public License. + + +Section 8 -- Interpretation. + + a. For the avoidance of doubt, this Public License does not, and + shall not be interpreted to, reduce, limit, restrict, or impose + conditions on any use of the Licensed Material that could lawfully + be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is + deemed unenforceable, it shall be automatically reformed to the + minimum extent necessary to make it enforceable. If the provision + cannot be reformed, it shall be severed from this Public License + without affecting the enforceability of the remaining terms and + conditions. + + c. No term or condition of this Public License will be waived and no + failure to comply consented to unless expressly agreed to by the + Licensor. + + d. Nothing in this Public License constitutes or may be interpreted + as a limitation upon, or waiver of, any privileges and immunities + that apply to the Licensor or You, including from the legal + processes of any jurisdiction or authority. + + +======================================================================= + +Creative Commons is not a party to its public +licenses. Notwithstanding, Creative Commons may elect to apply one of +its public licenses to material it publishes and in those instances +will be considered the “Licensor.” The text of the Creative Commons +public licenses is dedicated to the public domain under the CC0 Public +Domain Dedication. Except for the limited purpose of indicating that +material is shared under a Creative Commons public license or as +otherwise permitted by the Creative Commons policies published at +creativecommons.org/policies, Creative Commons does not authorize the +use of the trademark "Creative Commons" or any other trademark or logo +of Creative Commons without its prior written consent including, +without limitation, in connection with any unauthorized modifications +to any of its public licenses or any other arrangements, +understandings, or agreements concerning use of licensed material. For +the avoidance of doubt, this paragraph does not form part of the +public licenses. + +Creative Commons may be contacted at creativecommons.org. diff --git a/LICENSES/MIT.txt b/LICENSES/MIT.txt new file mode 100644 index 00000000..2071b23b --- /dev/null +++ b/LICENSES/MIT.txt @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) <year> <copyright holders> + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/NOTICES.md b/NOTICES.md new file mode 100644 index 00000000..4b09090a --- /dev/null +++ b/NOTICES.md @@ -0,0 +1,645 @@ +# Open Source License Disclosure + +## @docusaurus/core, @docusaurus/preset-classic, @docusaurus/theme-mermaid +MIT License + +Copyright (c) Facebook, Inc. and its affiliates. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +## @mdx-js/react +The MIT License (MIT) + +Copyright (c) 2017 Compositor and Vercel, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +## assert +Copyright Joyent, Inc. and other Node contributors. All rights reserved. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. + +## chalk +MIT License + +Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +## clsx +MIT License + +Copyright (c) Luke Edwards <luke.edwards05@gmail.com> (lukeed.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +## commander +(The MIT License) + +Copyright (c) 2011 TJ Holowaychuk <tj@vision-media.ca> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +## fast-csv +The MIT License + +Copyright (c) 2011-2019 C2FO + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +## follow-redirects +Copyright 2014–present Olivier Lalonde <olalonde@gmail.com>, James Talmage <james@talmage.io>, Ruben Verborgh + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +## fp-ts +MIT License + +Copyright (c) 2017-present Giulio Canti + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +## gtfs-realtime-bindings + 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. + +## jszip +Copyright (c) 2009-2016 Stuart Knightley, David Duponchel, Franz Buchinger, António Afonso + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +## langium +Copyright 2021 TypeFox GmbH + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +## mime-types +(The MIT License) + +Copyright (c) 2014 Jonathan Ong <me@jongleberry.com> +Copyright (c) 2015 Douglas Christopher Wilson <doug@somethingdoug.com> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +## monaco-editor +The MIT License (MIT) + +Copyright (c) 2016 - present Microsoft Corporation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +## monaco-languageclient +Copyright (c) 2018-2022 TypeFox GmbH (http://www.typefox.io) + +All rights reserved. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +## pg +MIT License + +Copyright (c) 2010 - 2021 Brian Carlson + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +## prism-react-renderer +MIT License + +Copyright (c) 2018 Formidable + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +## react, react-dom +MIT License + +Copyright (c) Meta Platforms, Inc. and affiliates. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +## sqlite3 +Copyright (c) MapBox +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. +- Neither the name "MapBox" nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +## tslib +Copyright (c) Microsoft Corporation. + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + +## vscode-languageclient, vscode-languageserver, vscode-languageserver-protocol +Copyright (c) Microsoft Corporation + +All rights reserved. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +## vscode-uri +The MIT License (MIT) + +Copyright (c) Microsoft + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +## exceljs +The MIT License (MIT) + +Copyright (c) 2014-2019 Guyon Roche + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/NOTICES.md.license b/NOTICES.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/NOTICES.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs-generator/.eslintrc.json b/apps/docs-generator/.eslintrc.json new file mode 100644 index 00000000..7707693a --- /dev/null +++ b/apps/docs-generator/.eslintrc.json @@ -0,0 +1,21 @@ +{ + "extends": ["../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "parserOptions": { + "project": ["apps/docs-generator/tsconfig.app.json"] + }, + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/apps/docs-generator/.eslintrc.json.license b/apps/docs-generator/.eslintrc.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs-generator/.eslintrc.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs-generator/package.json b/apps/docs-generator/package.json new file mode 100644 index 00000000..3dbc1ca5 --- /dev/null +++ b/apps/docs-generator/package.json @@ -0,0 +1,3 @@ +{ + "type": "module" +} diff --git a/apps/docs-generator/package.json.license b/apps/docs-generator/package.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs-generator/package.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs-generator/project.json b/apps/docs-generator/project.json new file mode 100644 index 00000000..96104229 --- /dev/null +++ b/apps/docs-generator/project.json @@ -0,0 +1,31 @@ +{ + "name": "docs-generator", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "apps/docs-generator/src", + "projectType": "application", + "targets": { + "build": { + "dependsOn": ["generate-language-server"], + "options": { + "main": "{projectRoot}/src/main.ts", + "tsConfig": "{projectRoot}/tsconfig.app.json" + } + }, + "start": { + "executor": "@nx/js:node", + "dependsOn": ["generate-language-server"], + "options": { + "watch": false, + "buildTarget": "docs-generator:build" + } + }, + "generate-language-server": { + "executor": "nx:run-commands", + "options": { + "commands": ["nx run language-server:generate"], + "parallel": false + } + } + }, + "tags": [] +} diff --git a/apps/docs-generator/project.json.license b/apps/docs-generator/project.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs-generator/project.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs-generator/src/main.ts b/apps/docs-generator/src/main.ts new file mode 100644 index 00000000..a0a35cd6 --- /dev/null +++ b/apps/docs-generator/src/main.ts @@ -0,0 +1,129 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { readFileSync, readdirSync, writeFileSync } from 'node:fs'; +import path, { join } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import { + type JayveeServices, + createJayveeServices, + getAllBuiltinBlockTypes, + getAllBuiltinConstraintTypes, + initializeWorkspace, +} from '@jvalue/jayvee-language-server'; +import { NodeFileSystem } from 'langium/node'; + +import { UserDocGenerator } from './user-doc-generator'; + +/** ESM does not know __filename and __dirname, so defined here */ +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +async function main(): Promise<void> { + const docsAppPath = join(__dirname, '..', '..', '..', 'apps', 'docs'); + const jayveeExamplesPath = join(__dirname, '..', '..', '..', 'example'); + + const services = createJayveeServices(NodeFileSystem).Jayvee; + await initializeWorkspace(services); + + generateBlockTypeDocs(services, docsAppPath); + generateConstraintTypeDocs(services, docsAppPath); + generateValueTypeDocs(services, docsAppPath); + generateExampleDocs(jayveeExamplesPath, docsAppPath); +} + +function generateBlockTypeDocs( + services: JayveeServices, + docsAppPath: string, +): void { + const blockTypes = getAllBuiltinBlockTypes( + services.shared.workspace.LangiumDocuments, + services.WrapperFactories, + ); + + const docsPath = join(docsAppPath, 'docs', 'user', 'block-types'); + + for (const blockType of blockTypes) { + const userDocBuilder = new UserDocGenerator(services); + const blockTypeDoc = userDocBuilder.generateBlockTypeDoc(blockType); + + const fileName = `${blockType.type}.md`; + writeFileSync(join(docsPath, fileName), blockTypeDoc, { + flag: 'w', + }); + console.info(`Generated file ${fileName}`); + } +} + +function generateConstraintTypeDocs( + services: JayveeServices, + docsAppPath: string, +): void { + const docsPath = join(docsAppPath, 'docs', 'user', 'constraint-types'); + const constraintTypes = getAllBuiltinConstraintTypes( + services.shared.workspace.LangiumDocuments, + services.WrapperFactories, + ); + + for (const constraintType of constraintTypes) { + const userDocBuilder = new UserDocGenerator(services); + const blockTypeDoc = + userDocBuilder.generateConstraintTypeDoc(constraintType); + + const fileName = `${constraintType.type}.md`; + writeFileSync(join(docsPath, fileName), blockTypeDoc, { + flag: 'w', + }); + console.info(`Generated file ${fileName}`); + } +} + +function generateValueTypeDocs( + services: JayveeServices, + docsAppPath: string, +): void { + const docsPath = join(docsAppPath, 'docs', 'user', 'value-types'); + const userDocBuilder = new UserDocGenerator(services); + const valueTypeDoc = userDocBuilder.generateValueTypesDoc( + services.ValueTypeProvider.Primitives.getAll(), + ); + + const fileName = `built-in-value-types.md`; + writeFileSync(join(docsPath, fileName), valueTypeDoc, { + flag: 'w', + }); + console.info(`Generated file ${fileName}`); +} + +function generateExampleDocs(examplesPath: string, docsAppPath: string): void { + const docsPath = join(docsAppPath, 'docs', 'user', 'examples'); + + for (const file of readdirSync(examplesPath)) { + if (file.endsWith('.jv')) { + const exampleFilePath = join(examplesPath, file); + const exampleModel = readFileSync(exampleFilePath); + + const exampleName = file.slice(0, -'.jv'.length); + const docFileName = `${exampleName}.md`; + const docContent = ` +--- +title: ${exampleName} +--- + +\`\`\`jayvee +${exampleModel.toString()} +\`\`\` + `.trim(); + writeFileSync(join(docsPath, docFileName), docContent, { + flag: 'w', + }); + console.info(`Generated example doc ${docFileName}`); + } + } +} + +main() + .then(() => console.log('Finished generating docs!')) + .catch((e) => console.error(e)); diff --git a/apps/docs-generator/src/user-doc-generator.ts b/apps/docs-generator/src/user-doc-generator.ts new file mode 100644 index 00000000..771aa31d --- /dev/null +++ b/apps/docs-generator/src/user-doc-generator.ts @@ -0,0 +1,280 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; + +import { + type BlockTypeWrapper, + type ConstraintTypeWrapper, + type ExampleDoc, + type IOType, + type JayveeBlockTypeDocGenerator, + type JayveeConstraintTypeDocGenerator, + type JayveeServices, + type JayveeValueTypesDocGenerator, + MarkdownBuilder, + type PrimitiveValueType, + type PropertySpecification, +} from '@jvalue/jayvee-language-server'; + +export class UserDocGenerator + implements + JayveeBlockTypeDocGenerator, + JayveeConstraintTypeDocGenerator, + JayveeValueTypesDocGenerator +{ + constructor(private services: JayveeServices) {} + + generateValueTypesDoc(valueTypes: PrimitiveValueType[]): string { + const builder = new UserDocMarkdownBuilder() + .docTitle('Built-in Value Types') + .generationComment() + .description( + ` +For an introduction to _value types_, see the [core concepts](../core-concepts). +_Built-in value types_ come with the basic version of Jayvee. +They are the basis for more restricted [_primitive value types_](./primitive-value-types) +that fullfil [_constraints_](./primitive-value-types#constraints).`.trim(), + 1, + ) + .heading('Available built-in value types', 1); + + valueTypes + .filter((valueType) => valueType.isReferenceableByUser()) + .forEach((valueType) => { + assert( + valueType.getUserDoc(), + `Documentation is missing for user extendable value type: ${valueType.getName()}`, + ); + builder + .heading(valueType.getName(), 2) + .description(valueType.getUserDoc() ?? '', 3) + .examples( + [ + { + code: ` +block ExampleTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "columnName" oftype ${valueType.getName()} + ]; +}`.trim(), + description: `A block of type \`TableInterpreter\` that + interprets data in the column \`columnName\` as \`${valueType.getName()}\`. + `.trim(), + }, + ], + 3, + ); + }); + + return builder.build(); + } + + generateBlockTypeDoc(blockType: BlockTypeWrapper): string { + const documentationService = + this.services.documentation.DocumentationProvider; + const blocktypeDocs = documentationService.getDocumentation( + blockType.astNode, + ); + const blocktypeDocsFromComments = + this.extractDocsFromComment(blocktypeDocs); + + const builder = new UserDocMarkdownBuilder() + .docTitle(blockType.type) + .generationComment() + .ioTypes(blockType.inputType, blockType.outputType) + .description(blocktypeDocsFromComments?.description) + .examples(blocktypeDocsFromComments?.examples); + + builder.propertiesHeading(); + Object.entries(blockType.getPropertySpecifications()).forEach( + ([key, property]) => { + const blocktypeProperty = blockType.astNode.properties.filter( + (p) => p.name === key, + )[0]; + if (blocktypeProperty === undefined) { + return; + } + + const propertyDocs = + documentationService.getDocumentation(blocktypeProperty); + const propDocsFromComments = this.extractDocsFromComment(propertyDocs); + + builder + .propertyHeading(key, 3) + .propertySpec(property) + .description(propDocsFromComments?.description, 4) + .validation(property.docs?.validation, 4) + .examples(propDocsFromComments?.examples, 4); + }, + ); + + return builder.build(); + } + + generateConstraintTypeDoc(constraintType: ConstraintTypeWrapper): string { + const documentationService = + this.services.documentation.DocumentationProvider; + const blocktypeDocs = documentationService.getDocumentation( + constraintType.astNode, + ); + const constraintTypeDocsFromComments = + this.extractDocsFromComment(blocktypeDocs); + + const builder = new UserDocMarkdownBuilder() + .docTitle(constraintType.type) + .generationComment() + .compatibleValueType(constraintType.on.getName()) + .description(constraintTypeDocsFromComments?.description) + .examples(constraintTypeDocsFromComments?.examples); + + builder.propertiesHeading(); + Object.entries(constraintType.getPropertySpecifications()).forEach( + ([key, property]) => { + builder + .propertyHeading(key, 3) + .propertySpec(property) + .description(property.docs?.description, 4) + .validation(property.docs?.validation, 4) + .examples(property.docs?.examples, 4); + }, + ); + + return builder.build(); + } + + private extractDocsFromComment(comment?: string | undefined): + | { + description: string | undefined; + examples: ExampleDoc[]; + } + | undefined { + if (comment === undefined) { + return undefined; + } + /* + Format: + + <description> + *@example* + <example description> + <example code> + */ + + const commentSections = comment + .split('*@example*') + .map((section) => section.trim()); + const examples = commentSections.slice(1).map((x) => { + const exampleLines = x.split('\n'); + return { + description: exampleLines[0] ?? '', + code: exampleLines.slice(1).join('\n'), + }; + }); + + return { + description: commentSections[0], + examples: examples, + }; + } +} + +class UserDocMarkdownBuilder { + private markdownBuilder = new MarkdownBuilder(); + + docTitle(blockType: string): UserDocMarkdownBuilder { + this.markdownBuilder + .line('---') + .line(`title: ${blockType}`) + .line('---') + .newLine(); + return this; + } + + generationComment(): UserDocMarkdownBuilder { + this.markdownBuilder + .comment( + 'Do NOT change this document as it is auto-generated from the language server', + ) + .newLine(); + return this; + } + + heading(heading: string, depth = 1): UserDocMarkdownBuilder { + this.markdownBuilder.heading(heading, depth); + return this; + } + + propertyHeading(propertyName: string, depth = 1): UserDocMarkdownBuilder { + this.markdownBuilder.heading(`\`${propertyName}\``, depth); + return this; + } + + propertySpec(propertySpec: PropertySpecification): UserDocMarkdownBuilder { + this.markdownBuilder.line(`Type \`${propertySpec.type.getName()}\``); + if (propertySpec.defaultValue !== undefined) { + this.markdownBuilder + .newLine() + .line(`Default: \`${JSON.stringify(propertySpec.defaultValue)}\``); + } + this.markdownBuilder.newLine(); + return this; + } + + ioTypes(inputType: IOType, outputType: IOType): UserDocMarkdownBuilder { + this.markdownBuilder + .line(`Input type: \`${inputType}\``) + .newLine() + .line(`Output type: \`${outputType}\``) + .newLine(); + return this; + } + + compatibleValueType(type: string): UserDocMarkdownBuilder { + this.markdownBuilder.line(`Compatible value type: ${type}`); + this.markdownBuilder.newLine(); + return this; + } + + description(text?: string, depth = 2): UserDocMarkdownBuilder { + if (text === undefined) { + return this; + } + this.markdownBuilder.heading('Description', depth).line(text).newLine(); + return this; + } + + propertiesHeading(): UserDocMarkdownBuilder { + this.markdownBuilder.heading('Properties', 2); + return this; + } + + validation(text?: string, depth = 2): UserDocMarkdownBuilder { + if (text === undefined) { + return this; + } + this.markdownBuilder.heading('Validation', depth).line(text).newLine(); + return this; + } + + examples(examples?: ExampleDoc[], depth = 2): UserDocMarkdownBuilder { + if (examples === undefined) { + return this; + } + for (const [index, example] of examples.entries()) { + this.markdownBuilder + .heading(`Example ${index + 1}`, depth) + .code(example.code, 'jayvee') + .line(example.description) + .newLine(); + } + return this; + } + + build(): string { + return this.markdownBuilder.build(); + } +} diff --git a/apps/docs-generator/tsconfig.app.json b/apps/docs-generator/tsconfig.app.json new file mode 100644 index 00000000..0adcbec8 --- /dev/null +++ b/apps/docs-generator/tsconfig.app.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "types": ["node"] + }, + "exclude": ["src/**/*.spec.ts", "src/**/*.test.ts"], + "include": ["src/**/*.ts"] +} diff --git a/apps/docs-generator/tsconfig.app.json.license b/apps/docs-generator/tsconfig.app.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs-generator/tsconfig.app.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs-generator/tsconfig.json b/apps/docs-generator/tsconfig.json new file mode 100644 index 00000000..3685ac7f --- /dev/null +++ b/apps/docs-generator/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../tsconfig.base.json", + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.app.json" + } + ], + "compilerOptions": { + "esModuleInterop": true + } +} diff --git a/apps/docs-generator/tsconfig.json.license b/apps/docs-generator/tsconfig.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs-generator/tsconfig.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/.eslintrc.json b/apps/docs/.eslintrc.json new file mode 100644 index 00000000..505b12ce --- /dev/null +++ b/apps/docs/.eslintrc.json @@ -0,0 +1,28 @@ +{ + "extends": ["../../.eslintrc.json"], + "ignorePatterns": [ + "!**/*", + "**/.docusaurus/**", + "**/theme/prism-jayvee.js", + "*.config.js", + "**/prism-include-languages.js", + "**/sidebars.js" + ], + "parserOptions": { + "project": ["apps/docs/tsconfig.app.json"] + }, + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/apps/docs/.eslintrc.json.license b/apps/docs/.eslintrc.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/.eslintrc.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/.gitignore b/apps/docs/.gitignore new file mode 100644 index 00000000..c9dea7f4 --- /dev/null +++ b/apps/docs/.gitignore @@ -0,0 +1,6 @@ +# SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +# +# SPDX-License-Identifier: AGPL-3.0-only + +# file generated by Langium +src/theme/prism-jayvee.js diff --git a/apps/docs/README.md b/apps/docs/README.md new file mode 100644 index 00000000..403d70ad --- /dev/null +++ b/apps/docs/README.md @@ -0,0 +1,24 @@ +<!-- +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only +--> + +# Jayvee Docs + +This app documents the Jayvee language based on the [docusaurus project](https://docusaurus.io/). + +## Development + +Consider these commands at root level of the repo. + +### Serve docs on local machine (with hot reload) +```bash +npx nx serve docs +``` + +### Build static files for deployment +```bash +npx nx build docs +``` + diff --git a/apps/docs/babel.config.js b/apps/docs/babel.config.js new file mode 100644 index 00000000..a19f113d --- /dev/null +++ b/apps/docs/babel.config.js @@ -0,0 +1,7 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +module.exports = { + presets: [require.resolve('@docusaurus/core/lib/babel/preset')], +}; diff --git a/apps/docs/docs/dev/01-intro.mdx b/apps/docs/docs/dev/01-intro.mdx new file mode 100644 index 00000000..cdb9efe1 --- /dev/null +++ b/apps/docs/docs/dev/01-intro.mdx @@ -0,0 +1,64 @@ +--- +sidebar_position: 1 +--- + +# Introduction for Jayvee Developers + +import MascotImageUrl from '@site/static/img/mascots/mascot3.png'; + +<div style={{ display: 'flex', flexDirection: 'row', flexWrap: 'none', alignItems: 'center'}}> + <div> + <img src={MascotImageUrl} width="400px" style={{ float: 'left' }} /> + </div> + + <span className={"text--center"}> + <h2>"On the tracks of Jayvee's internals"</h2> + </span> +</div> + +## How to contribute + +In order to contribute to the Jayvee project, please have a look at our [contribution guide](https://github.com/jvalue/jayvee/blob/main/CONTRIBUTING.md). Before planning a contribution, please read the [design principles](./05-design-principles.md) and consider if your changes fit the vision expressed there. + +The overall project is licensed under the `AGPL-3.0-only` license and is compliant to the [REUSE Specification](https://reuse.software/spec/) by the [Free Software Foundation Europe](https://fsfe.org/). +Because of this, contributions are required to adhere to the license and follow that specification. +More details on this topic can be found [here](./02-dev-processes/03-licensing-and-copyright.md). + +And last but not least, please read and follow our [code of conduct](https://github.com/jvalue/jayvee/blob/main/CODE_OF_CONDUCT.md). + +## Project overview + +The Jayvee repository is located [here](https://github.com/jvalue/jayvee) on GitHub. +It uses an [Nx mono-repository](https://nx.dev/) setup and contains all related projects, most notably the Jayvee language server and interpreter. +Please have a look at the [architecture overview](./03-architecture-overview.md) for more details. + +## Prerequisites + +Node.js and npm are required for development. +It is recommended to use their LTS version to avoid any potential compatibility issues. +Also, refer to this [quick start guide](https://github.com/jvalue/jayvee#development-quickstart) for developers. + +In order to run the Jayvee VS Code extension during development, VS Code is required as IDE. +Using VS Code also allows installing the [Langium VS Code extension](https://marketplace.visualstudio.com/items?itemName=langium.langium-vscode) for better IDE support when working with Langium grammar files. + +## Resources for getting started + +### Langium + +- [**Langium documentation**](https://langium.org/docs/) +- Practical examples using Langium: + - [Langium examples](https://github.com/langium/langium/tree/main/examples): Official example languages by Langium + - [Langium's grammar language](https://github.com/langium/langium/tree/main/packages/langium): The implementation of Langium's own grammar language + - [Language server for SQL](https://github.com/langium/langium-sql): An implementation of a language server for SQL + - [MiniLogo DSL](https://github.com/langium/langium-minilogo): A domain-specific language for drawing pictures + - [Lox language](https://github.com/langium/langium-lox): An implementation of the Lox scripting language (also see the "Crafting Interpreters" book below) + - [SimpleUI DSL](https://github.com/TypeFox/langium-ui-framework): A domain-specific language for generating user interfaces + - [STPA-DSL](https://github.com/kieler/stpa): A domain-specific language for System-Theoretic Process Analysis +- [Langium playground](https://langium.org/playground/): A tool for testing Langium grammars and visualizing resulting ASTs +- [Typefox blog](https://www.typefox.io/blog/): A blog by the company behind Langium, mostly posting Langium-related content. + +### Building compilers / interpreters / DSLs + +- [Crafting Interpreters](https://craftinginterpreters.com/contents.html): A book by Robert Nystrom on implementing an interpreter for the Lox scripting language +- [DSL Engineering](https://voelter.de/dslbook/markusvoelter-dslengineering-1.0.pdf): A book by Markus Voelter on designing, implementing and using domain-specific languages +- [Awesome Compilers](https://github.com/aalhour/awesome-compilers#readme): A list featuring many resources on compilers and interpreters diff --git a/apps/docs/docs/dev/01-intro.mdx.license b/apps/docs/docs/dev/01-intro.mdx.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/docs/dev/01-intro.mdx.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/docs/dev/02-dev-processes/01-rfc-process.md b/apps/docs/docs/dev/02-dev-processes/01-rfc-process.md new file mode 100644 index 00000000..07444085 --- /dev/null +++ b/apps/docs/docs/dev/02-dev-processes/01-rfc-process.md @@ -0,0 +1,13 @@ +--- +title: Language Design Process (RFCs) +sidebar_position: 1 +--- + +We use RFCs to discuss changes to the language before implementing them. You can have a look at all closed (accepted / rejected) RFCs [here](https://github.com/jvalue/jayvee/pulls?q=is%3Apr+is%3Aclosed+RFC+), and all RFCs under discussion [here](https://github.com/jvalue/jayvee/pulls?q=is%3Apr+is%3Aopen+RFC). + +If you want to contribute an RFC please follow these steps: +1. Make a copy of the **template** at `rfc/0000-rfc-template.md` and place it into the `rfc` folder. +2. Create a draft for the RFC on a new branch. Follow the `TODOs` in template to do so. +3. Open a pull request with prefix `RFC <number>` in the title. +4. Address the reviews. Consider opening a new PR if major things need to be addressed and the discussion log becomes too confusing. +5. Once accepted, create an issue with the necessary steps to implement the RFC. \ No newline at end of file diff --git a/apps/docs/docs/dev/02-dev-processes/01-rfc-process.md.license b/apps/docs/docs/dev/02-dev-processes/01-rfc-process.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/docs/dev/02-dev-processes/01-rfc-process.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/docs/dev/02-dev-processes/02-debug-vs-code-extension.md b/apps/docs/docs/dev/02-dev-processes/02-debug-vs-code-extension.md new file mode 100644 index 00000000..5fbcf07c --- /dev/null +++ b/apps/docs/docs/dev/02-dev-processes/02-debug-vs-code-extension.md @@ -0,0 +1,25 @@ +--- +title: Debugging via the VS Code extension +sidebar_position: 2 +--- + +The VS Code extension of Jayvee can be used to interactively debug the language server. +During development, when using VS Code as IDE, another instance of VS Code can be opened which has the most recent, locally built VS Code extension loaded. +This can be achieved using the launch configuration `Run extension` the `Run and Debug` side-menu of VS Code or by pressing the `F5` key if that launch configuration is already selected there. + +Note that there is no file watching mechanism involved. +So in order to reflect changes to the source code, the additional VS Code instance has to be **closed and reopened** as described above. + +## How to attach a debugger + +1. Use the launch configuration `Run extension` to open the additional VS Code instance with the extension loaded +2. Use the launch configuration `Attach to Language Server` to attach the debugger + +Any set breakpoints should now be marked as active and pause the execution once they are reached. + +## How to view logs of the language server + +In the additional VS Code instance, it is possible to view the logs of the language server. +They might also be helpful for debugging since any uncaught errors are logged with their stack trace by the Langium framework. + +To view the logs, open the bottom panel called `Output` and select `Jayvee` in the dropdown menu. diff --git a/apps/docs/docs/dev/02-dev-processes/02-debug-vs-code-extension.md.license b/apps/docs/docs/dev/02-dev-processes/02-debug-vs-code-extension.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/docs/dev/02-dev-processes/02-debug-vs-code-extension.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/docs/dev/02-dev-processes/03-licensing-and-copyright.md b/apps/docs/docs/dev/02-dev-processes/03-licensing-and-copyright.md new file mode 100644 index 00000000..201e21ca --- /dev/null +++ b/apps/docs/docs/dev/02-dev-processes/03-licensing-and-copyright.md @@ -0,0 +1,69 @@ +--- +title: Licensing and copyright +sidebar_position: 3 +--- + +The [Jayvee repository](https://github.com/jvalue/jayvee) is REUSE compliant, meaning that it fulfills the +[REUSE Specification](https://reuse.software/spec/) by the [Free Software Foundation Europe](https://fsfe.org/) (FSFE). +This makes clear under which license each project file is licensed under and who owns the copyright, not only for +humans but also for machines. + +This is done by explicitly adding copyright and licensing information to each file of the project. This is achieved +by either using a comment header or a separate `*.license` file in case comments are not possible. + +See <https://reuse.software/> more information. + +## What license is used in this project and who is the copyright holder? + +The entire project is licensed under [AGPL-3.0-only](https://spdx.org/licenses/AGPL-3.0-only.html), the +license file can be found [here](https://github.com/jvalue/jayvee/blob/main/LICENSES/AGPL-3.0-only.txt). +The copyright holder is the [Friedrich-Alexander-Universität Erlangen-Nürnberg](https://www.fau.eu/). + +## How to submit a REUSE compliant contribution? + +In case you want to contribute to the project, you will need to ensure that all of your contributed files are REUSE +compliant. In order to achieve this, you need to add a key-value pair for both copyright and licensing information +following this schema: + +``` +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only +``` + +In case a file allows comments, use single-line comments to add the copyright and licensing information at the top +of the file. Otherwise, create a corresponding `*.license` file with the text above as its content. You can have a +look at some existing project files to get an impression on it is done in practice. + +For files with common file extensions, you can use the [reuse CLI tool](https://github.com/fsfe/reuse-tool) to add +licensing and copyright information automatically. + +For more details, you can have a look at the [Getting Started tutorial](https://reuse.software/tutorial/) on the REUSE +website. + +## How to validate REUSE compliance? + +When you make a contribution and open a new pull request, the CI checks whether your contribution is REUSE compliant +using the [reuse CLI tool](https://github.com/fsfe/reuse-tool). + +In order to validate REUSE compliance in your local development environment, you have to install the +[reuse CLI tool](https://github.com/fsfe/reuse-tool) and run the following command in the projects' root folder: + +```bash +reuse lint +``` + +You can also set up a pre-commit hook, so the above command is run before each commit. +See [here](https://reuse.readthedocs.io/en/latest/readme.html#run-as-pre-commit-hook) for details on how to set it up. + +## How to hide `*.license` files in IDEs + +During development, the file explorer of your IDE may be cluttered due to the numerous `*.license` files in the +project. Luckily, most IDEs allow hiding certain files, e.g. by specifying a pattern to exclude them from the +explorer. + +Below, you can find instructions on how to hide `*.license` files in commonly used IDEs: +- **Visual Studio Code**: Add `**/*.license` to the +[`files.exclude` setting](https://code.visualstudio.com/docs/getstarted/userinterface#_explorer) +- **WebStorm**: Add `*.license` to the +[excluded files setting](https://www.jetbrains.com/help/webstorm/configuring-project-structure.html#exclude-by-pattern) diff --git a/apps/docs/docs/dev/02-dev-processes/03-licensing-and-copyright.md.license b/apps/docs/docs/dev/02-dev-processes/03-licensing-and-copyright.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/docs/dev/02-dev-processes/03-licensing-and-copyright.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/docs/dev/02-dev-processes/04-release-jayvee-version.md b/apps/docs/docs/dev/02-dev-processes/04-release-jayvee-version.md new file mode 100644 index 00000000..374f3468 --- /dev/null +++ b/apps/docs/docs/dev/02-dev-processes/04-release-jayvee-version.md @@ -0,0 +1,22 @@ +--- +title: Releasing a new Jayvee version +sidebar_position: 4 +--- + +## Version Numbers + +In this early stage of the project we do not yet follow [semantic versioning](https://semver.org/) since we expect the introduction of breaking changes frequently. +To indicate that, we only release alpha versions where the `version` is incremented with every release. +- For the npm packages, we use the version `0.0.<version>`. +- For the GitHub releases, we use the git tag `v0.0.<version>-alpha`. + +## Jayvee Release Procedure + +For releasing a new version of Jayvee, you need to complete the following steps: + +1. Increment the version in the `package.json` file. +2. Run `npm i` to update the `package-lock.json`. +3. Run `npx nx run docs:version-snapshot` to generate a snapshot of the docs for the previous version. +4. If you are on a feature or dev branch, merge into main. +5. Create a GitHub release on the main branch. Attach a changelog. +6. The CI/CD will deal with the rest. diff --git a/apps/docs/docs/dev/02-dev-processes/04-release-jayvee-version.md.license b/apps/docs/docs/dev/02-dev-processes/04-release-jayvee-version.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/docs/dev/02-dev-processes/04-release-jayvee-version.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/docs/dev/02-dev-processes/_category_.json b/apps/docs/docs/dev/02-dev-processes/_category_.json new file mode 100644 index 00000000..c8f51245 --- /dev/null +++ b/apps/docs/docs/dev/02-dev-processes/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Development Process", + "position": 2, + "link": { + "type": "generated-index", + "description": "Here you can find general processes around developing Jayvee." + } +} diff --git a/apps/docs/docs/dev/02-dev-processes/_category_.json.license b/apps/docs/docs/dev/02-dev-processes/_category_.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/docs/dev/02-dev-processes/_category_.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/docs/dev/03-architecture-overview.md b/apps/docs/docs/dev/03-architecture-overview.md new file mode 100644 index 00000000..b609a613 --- /dev/null +++ b/apps/docs/docs/dev/03-architecture-overview.md @@ -0,0 +1,51 @@ +--- +title: Architecture overview +sidebar_position: 4 +--- + +Jayvee has a clear separation between the language itself and its execution. +This guide gives an overview of the overall architecture. + +## Language Server + +On the pure language side, the central project is the [language server](https://github.com/jvalue/jayvee/tree/main/libs/language-server) which is heavily built upon the [Langium framework](https://langium.org/). +It contains the syntax definition (i.e. the grammar) and is capable of performing static semantic analysis on models, so invalid models can be rejected and errors are reported to the user. +It uses the [Language Server Protocol](https://microsoft.github.io/language-server-protocol/) (LSP) for communicating with IDEs in order to provide common features such as diagnostics, auto completion and much more. + +**Note:** The [Langium framework](https://langium.org/) generate TypeScript files for the abstract syntax tree (AST), based on the grammar specification. +The following locations might be especially helpful to understand the grammar and its AST: + +- The Langium grammar files (see [here](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/grammar) or locally at `libs/language-server/src/grammar`; with `.langium` file ending). These files define the **syntax of the language**. +- The generated TypeScript AST files (execute `npm run generate` to generate them at `libs/language-server/src/lib/ast/generated` in your local repository). These files include **TypeScript interfaces for AST nodes** (e.g., `BlockDefinition`) and **guard methods** (e.g., `isBlockDefinition`). + They reflect the input of the grammar files regarding naming. +- The remaining source files of the language server implement the language server protocol (LSP) and the additional validations beyond the syntax of Jayvee. + +## Interpreter + +The Jayvee [interpreter](https://github.com/jvalue/jayvee/tree/main/apps/interpreter) on the other hand is capable of running Jayvee models. +Therefore, it uses the language server as a library to perform lexing, parsing and semantic analysis on given models. +In case no errors were found during these phases, it executes the model by interpreting it. +This means that models are not compiled to any other language, like a compiler does, but rather directly executed on the fly without any code generation involved. +The interpreter comes with a command-line interface (CLI) for users, so they are able to execute Jayvee models easily. + +The following diagram visualizes the architecture described so far: + +```mermaid +graph LR +model[Jayvee Model] --> lexical(Lexical Analysis) +subgraph Jayvee Interpreter + subgraph Jayvee Language Server + subgraph Langium Framework + lexical --> syntax(Syntax Analysis) + end + syntax --> semantic(Semantic Analysis) + end + semantic --> interpretation(Interpretation) +end +``` + +## Jayvee Extensions + +Lastly, there are Jayvee extensions for adding additional features to Jayvee. +See [here](./04-guides/06-jayvee-extensions.md) for more details. +It is worth pointing out that extensions also follow the separation between language and execution described above. diff --git a/apps/docs/docs/dev/03-architecture-overview.md.license b/apps/docs/docs/dev/03-architecture-overview.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/docs/dev/03-architecture-overview.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/docs/dev/04-guides/01-jayvee-grammar.md b/apps/docs/docs/dev/04-guides/01-jayvee-grammar.md new file mode 100644 index 00000000..356fb09d --- /dev/null +++ b/apps/docs/docs/dev/04-guides/01-jayvee-grammar.md @@ -0,0 +1,34 @@ +--- +title: The Jayvee grammar +sidebar_position: 1 +--- + +The grammar of Jayvee describes the syntax and structure of the language. +It is located in the language server project. +The grammar itself is written in [Langium's own grammar language](https://langium.org/docs/grammar-language/) which is similar to [Xtext](https://www.eclipse.org/Xtext/) grammars. +Such grammar files are easily identifiable via their `.langium` file extension. + +The grammar is used to generate TypeScript interfaces that represent the Abstract Syntax Tree (AST) and different, semantically equivalent files to define syntax highlighting for different applications. +For instance, a [TextMate](https://macromates.com/manual/en/language_grammars) file is generated for the syntax highlighting in the Jayvee VS Code extension whereas a [Monarch](https://microsoft.github.io/monaco-editor/monarch.html) file is generated for the syntax highlighting in the Monaco editor. + +For further information on the grammar language of Langium, visit the corresponding [Langium documentation](https://langium.org/docs/grammar-language/). + +## Working with the grammar + +To run the code generation, either use `npm run generate` for solely the code generation or `npm run build` for an entire build. The code generation also generates further code, like the standard library. + +Whenever the grammar is changed and the code generation is run during development, it is advisory to **close and reopen the IDE**, so the changes are noticed and the file indexing is updated. + +## How to rename AST nodes + +Renaming AST nodes is not as straight forward as one might initially assume. +When renaming individual rules in the grammar, just the generated TypeScript code in `ast.ts` will reflect the renaming, but not the rest of the codebase. + +The following steps explain how to rename an AST node, so the change is reflected in the entire codebase. As an example, an AST node called `A` is supposed to be renamed to `B`: + +- Open the `ast.ts` file in the language server project +- Locate the `A` interface / type and the `isA` typeguard +- Use the rename feature by the IDE to perform the renaming from `A` to `B` and from `isA` to `isB` +- Open the grammar and locate the `A` rule +- Use the rename feature by the Langium VS Code extension to perform the renaming from `A` to `B` +- Run `npm run generate` diff --git a/apps/docs/docs/dev/04-guides/01-jayvee-grammar.md.license b/apps/docs/docs/dev/04-guides/01-jayvee-grammar.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/docs/dev/04-guides/01-jayvee-grammar.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/docs/dev/04-guides/02-working-with-the-ast.md b/apps/docs/docs/dev/04-guides/02-working-with-the-ast.md new file mode 100644 index 00000000..bb5a300a --- /dev/null +++ b/apps/docs/docs/dev/04-guides/02-working-with-the-ast.md @@ -0,0 +1,150 @@ +--- +title: Working with the AST +sidebar_position: 2 +--- + +The nodes of the Abstract Syntax Tree (AST) consist of types and interfaces generated from the language grammar. +See [here](./01-jayvee-grammar.md) for more information on that topic. + +The following sections provide practical guides and tips for working with nodes of the AST. + +## Dealing with potentially incomplete AST nodes + +According to the generated interfaces, properties of AST nodes are never `undefined`. +In practice however, this is not always the case. + +For example, consider the language server being confronted with an incomplete Jayvee model with syntax errors. +In such cases, properties of AST nodes are in fact `undefined`, despite their interface definition. +In the interpreter however, after lexical and syntactic analysis succeeded, it can be assumed that all AST nodes are complete (i.e. they have no undefined properties) and even that all references are resolved. + +In order to avoid accessing properties of AST nodes that are potentially undefined, it is recommended to access them via the [`?.` operator](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#optional-chaining) rather than the regular `.` operator. +Note that this might result in a conflict with the ESLint rule [`@typescript-eslint/no-unnecessary-condition`](https://typescript-eslint.io/rules/no-unnecessary-condition/), so it has to be disabled manually for such cases. + +For disabling the rule in an entire file, place this comment below the copyright and licensing header: + +```typescript +/* eslint-disable @typescript-eslint/no-unnecessary-condition */ +``` + +For just a single line, place this comment above that particular line: + +```typescript +// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition +``` + +<details> + +<summary>Full example</summary> + +Consider an exemplary AST node `A` with a property `x` of type `string`. To access that property safely: + +```typescript +import { A } from './ast' + +const astNode: A; + +// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition +const property: string | undefined = astNode?.x; +``` + +</details> + +## Usage of `assertUnreachable` + +Most times, it is beneficial to make case distinctions exhaustive, especially when working with AST nodes, properties with a [union literal type](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types) or enums. +Exhaustiveness in this context means, that the TypeScript compiler is supposed to yield an error if a case distinction does not cover all possibilities. + +Langium offers a function called `assertUnreachable` which is capable of enforcing exhaustiveness and producing compiler errors in case of violations. See the following examples to get an idea on how to use it in practice: + +<details> + +<summary>Example for an exhaustive switch statement on a union literal type</summary> + +```typescript +import { assertUnreachable } from 'langium'; + +const operator: '+' | '-'; + +switch(operator) { + case '+': { + // ... + break; + } + case '-': { + // ... + break; + } + default: { + // To ensure the switch being exhaustive on `operator`: + assertUnreachable(operator); + } +} +``` + +</details> + +<details> + +<summary>Example for an exhaustive if-elseif-else cascade using typeguards</summary> + +Consider the exemplary AST nodes `A`, `B` and `C` and that `A = B | C`: + +```typescript +import { assertUnreachable } from 'langium'; +import { A, B, isB, C, isC } from './ast' + +const astNode: A; + +if (isB(astNode)) { + // `astNode` has type `B` here +} else if (isC(astNode)) { + // `astNode` has type `C` here +} else { + // To ensure the if-elseif-else cascade being exhaustive on `astNode`: + assertUnreachable(astNode); +} +``` + +</details> + +## Usage of `assert` for expressing runtime expectations + +During development, it may occur that a certain condition is expected to **be always true** at runtime. +For example, an AST node being of a certain type or a property being defined. +The type system of TypeScript is not always able to infer such facts, so developers may have to express these expectations explicitly in their code. +Examples are [type assertions](https://www.typescriptlang.org/docs/handbook/advanced-types.html) or the [non-null assertion operator](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html#non-null-assertion-operator). +Their usage may be problematic in case the condition does not always hold, e.g. due to a bug in the program or a wrong expectation by the programmer. +In such cases, it is hard to locate the origin and debug the program because such operations are erased in the compiled JavaScript code. + +To avoid these issues, it is recommended to express such expectations as boolean expressions that are actually validated at runtime. +This can be easily achieved by using the `assert` function. +It evaluates a given boolean expression and throws an `AssertionError` in case it evaluates to `false`. +After calling `assert`, the type system of TypeScript assumes the condition to be `true` and afterwards narrows types accordingly. + +Here is an example of how to use it in practice: + +```typescript +// We use the `assert` function from the `assert` library, not `node:assert` (to preserve browser compatibility) + +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; + +import { A, B, isB } from './ast'; + +const astNode: A; +assert(isB(astNode)); +// Here `astNode` has type `B` + +const referenced = astNode?.reference?.ref; +assert(referenced !== undefined); +// Here `referred` is not `undefined` +``` + +## AST wrapper classes + +The generated interfaces for AST nodes in `ast.ts` are only meant to represent the AST structurally, they don't define any behavior. +Also, in case of syntactic sugar, there may be different kinds of AST nodes representing the same semantic language concept. + +To cope with this problem, there is the concept of an `AstNodeWrapper`. +An AST node wrapper is capable of wrapping AST nodes that represent the same semantic language concept and adding behavior to them via custom methods. +To get an impression on how this can be done in practice, please have a look at the [`PipeWrapper` class](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/lib/ast/wrappers/pipe-wrapper.ts) and the [`AstNodeWrapper` interface](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/lib/ast/wrappers/ast-node-wrapper.ts). diff --git a/apps/docs/docs/dev/04-guides/02-working-with-the-ast.md.license b/apps/docs/docs/dev/04-guides/02-working-with-the-ast.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/docs/dev/04-guides/02-working-with-the-ast.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/docs/dev/04-guides/03-validation-and-diagnostics.md b/apps/docs/docs/dev/04-guides/03-validation-and-diagnostics.md new file mode 100644 index 00000000..328d5728 --- /dev/null +++ b/apps/docs/docs/dev/04-guides/03-validation-and-diagnostics.md @@ -0,0 +1,64 @@ +--- +title: Validation and diagnostics +sidebar_position: 3 +--- + +Validation in the context of Jayvee can be understood as semantic analysis of Jayvee models. +The purpose is to uncover errors in models besides lexing, parsing and linking errors that the Langium framework already detects out of the box. +In case such errors are found, the language server makes the IDE display a diagnostic. +This means that the location of the error is underlined and an error message is shown. + +For example, each pipeline is expected to contain at least one starting block that requires no input. +Therefore, the language server analyzes every pipeline in a Jayvee model and checks for the presence of such a starting block. +In case no such block is found, the IDE underlines the pipeline definition in a red color and displays an error message. + +## How to implement validation + +### For arbitrary AST nodes + +In the file [`validation-registry.ts`](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/lib/validation/validation-registry.ts) of the language server, there is a registry containing validation functions for various AST nodes. +A validation function provided for a certain kind of AST node is called for every occurrence of such a node in a Jayvee model. +E.g. when registering a validation function for `PipelineDefinition` nodes, that function gets called once for each pipeline. + +A validation function always operates on a single, concrete AST node. +Besides the AST node, a `ValidationContext` object is passed to the function for reporting diagnostics via its `accept` method. + +The overall validation for a specific kind of AST node is usually subdivided into smaller checks that are called sequentially. +This potentially allows aborting the validation early, i.e. before all checks were run. +Aborting early may be useful if errors were reported during previous checks. +This can be determined via the `ValidationContext` which keeps track whether any errors occurred so far. + +A practical example where this plays a role are blocks: +When the type of a block is unknown to the language server, it makes no sense to validate inputs / outputs and properties of that block. +So, in case an error was reported during the check of the block type, the validation of that block is discontinued at that point. + +### For properties of blocks and constraints + +Validating property values of blocks / constraints works a bit differently because their individual validation is already incorporated into the overall validation framework. + +In general, the validation logic for a property values is located in the meta information of the block type / constraint type. +There, properties are defined using the `PropertySpecification` interface. +In order to add validation logic to a certain property, a validation function needs to be added to that property. + +For more complex validations that need to take multiple property values into account, it is also possible to provide a more general validation function which operates on the entire `PropertyBody` AST node. + +See [`text-range-selector-meta-inf.ts`](https://github.com/jvalue/jayvee/blob/main/libs/extensions/std/lang/src/text-range-selector-meta-inf.ts) for an example which includes both cases described above. + +## Reporting diagnostics + +During validation, diagnostics can be reported by calling the `accept` method of the given `ValidationContext` object. + +The call requires the severity of the diagnostic (`error`, `warning`, `info` or `hint`), the error message and its location. +The location is described by a `DiagnosticInfo` object which contains the affected AST node and optionally some further restrictions. +For more details, have a look at the [`DiagnosticInfo` interface](https://github.com/langium/langium/blob/main/packages/langium/src/validation/validation-registry.ts) by Langium. + +## Common issues + +When working on validations, a diagnostic with the following message may unexpectedly occur: `An error occurred during validation: ...` + +Such a diagnostic is generated by the Langium framework if an error object is thrown within the validation. +In most cases, the reason is the access of an `undefined` AST node property or an `AssertionError`. +For more details on such errors and how to avoid them, have a look at the documentation on [how to work with the AST](./02-working-with-the-ast.md). + +One way locate the origin of such errors is to debug the Jayvee VS Code extension, see [here](../02-dev-processes/02-debug-vs-code-extension.md) for more details. +Another possibility is to debug the interpreter and set appropriate breakpoints in the language server codebase. diff --git a/apps/docs/docs/dev/04-guides/03-validation-and-diagnostics.md.license b/apps/docs/docs/dev/04-guides/03-validation-and-diagnostics.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/docs/dev/04-guides/03-validation-and-diagnostics.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/docs/dev/04-guides/04-expressions-and-operators.md b/apps/docs/docs/dev/04-guides/04-expressions-and-operators.md new file mode 100644 index 00000000..d201864e --- /dev/null +++ b/apps/docs/docs/dev/04-guides/04-expressions-and-operators.md @@ -0,0 +1,151 @@ +--- +title: Expressions and operators +sidebar_position: 4 +--- + +Jayvee supports arbitrarily nested expressions and offers a variety of different operators. +Such expressions are similar to those used in programming languages, and they are intended to be read and understood intuitively. + +The following sections explain the definition of expressions in the language grammar, their type inference mechanism, and how the evaluation of expressions works. +As a small practical example, you may also have a look at the [arithmetics example by Langium](https://github.com/langium/langium/tree/main/examples/arithmetics) which has a heavy focus on mathematical expressions and functions. + +## Expressions in the grammar + +Expressions in the Jayvee grammar are defined in [`expression.langium`](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/grammar/expression.langium) and consist of operators (unary / binary / ternary) and literals. +Unary operators only have a single operand (e.g. the `not` operator), binary operators require two operands (e.g. the `*` operator) and ternary operators require three operands. + +The grammar is written in a way that literals end up in the leaves of the resulting AST and the nodes above represent the operators. + +As an example, have a look at the following AST structure of the expression `(-3 + 5) * 7`: + +```mermaid +classDiagram + +class `:BinaryOperator` { + operator = '*' +} + +%% Uses spaces in the name so it is unique +class ` :BinaryOperator` { + operator = '+' +} + +class `:UnaryOperator` { + operator = '-' +} + +%% Uses spaces in the name so it is unique +class ` :NumericLiteral` { + value = 3 +} + +%% Uses spaces in the name so it is unique +class ` :NumericLiteral` { + value = 5 +} + +class `:NumericLiteral` { + value = 7 +} + +`:BinaryOperator` --> ` :BinaryOperator` : leftOperand +`:BinaryOperator` --> `:NumericLiteral` : rightOperand +` :BinaryOperator` --> `:UnaryOperator` : leftOperand +`:UnaryOperator` --> ` :NumericLiteral` : operand +` :BinaryOperator` --> ` :NumericLiteral` : rightOperand +``` + +The AST is constructed in a way that ensures an unambiguous evaluation order when using depth-first search. +This implies that the AST structure already reflects the precedence and associativity of the operators, and that parentheses do not need to be explicitly represented. + +For more details on how such a grammar for expressions can be realized, see the [official Langium documentation](https://langium.org/docs/grammar-language/#tree-rewriting-actions), [this blog post](https://www.typefox.io/blog/parsing-expressions-with-xtext) and the following two sections which discuss the precedence and associativity of operators. + +### Precedence of operators + +The grammar implicitly defines the precedence of operators. +The operator precedence defines the order in which operators are evaluated within an expression. +For example, multiplication has a higher precedence than addition, which leads to multiplications being performed before additions. +In order to manually override such precedence conventions, parentheses can be used in expressions. + +The diagram below shows a more extensive precedence hierarchy, focused on common arithmetic operators. +Note that the hierarchy is arranged in ascending order, according to the operator precedence. +Such an order is similar to how operators in the actual Jayvee grammar are arranged: + +```mermaid +graph TD + subgraph BinaryOperators + add/sub(Addition Operator / Subtraction operator) --> mul/div(Multiplication Operator / Division Operator) + mul/div --> exp/root(Exponential Operator / Root Operator) + end + subgraph UnaryOperators + exp/root --> plus/minus(Plus Operator / Minus Operator) + end + plus/minus --> literal(Numeric Literal) +``` + +In the Jayvee grammar, every level of precedence is represented by a single grammar rule. +Within each such rule, the grammar rule which defines the operators with the next higher precedence is referenced. +E.g. the `AdditiveExpression` rule refers to the `MultiplicativeExpression` rule because multiplicative operators are supposed to have the next higher precedence. + +In order to alter the precedence of operators, their hierarchy of grammar rules has to be adjusted accordingly in [`expression.langium`](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/grammar/expression.langium). + +### Associativity of operators + +The associativity of operators defines the order in which operators with the same precedence are evaluated when they appear in succession without parentheses. +Operators may be either **left-associative**, **right-associative** or **non-associative**. +For example, depending on the associativity of the binary `+` operator, the expression `a + b + c` has different semantics: + +- Left-associative: `(a + b) + c` (evaluation from left to right) +- Right-associative: `a + (b + c)` (evaluation from right to left) +- Non-associative: _syntax error_, the operator cannot be chained + +The associativity of operators is also defined in the Jayvee grammar, more specifically by the structure of the grammar rules that define operators. +For details on how to encode the different kinds of associativity in grammar rules, have a look at the "Associativity" section near the end of [this blog post](https://www.typefox.io/blog/parsing-expressions-with-xtext). +The patterns described in the linked article can be found in [`expression.langium`](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/grammar/expression.langium), so each operator grammar rule encodes its own associativity. + +## Typing of expressions + +Jayvee has a mechanism for inferring and validating the type of a given expression. +This is achieved using a recursive algorithm which performs depth-first search on a given expression: + +The base case for the algorithm are literals, i.e. the tree leaves of an expression. +Depending on the type of literal, their type can be inferred trivially (in case of value literals) or otherwise from the context where the expression is located. + +For operators, the types of their operands are first inferred via recursion, and then it is checked whether they are supported by the operator. +Next, given the operand types, the resulting type is computed. +Such behavior is defined in a _type computer class_ located [here](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/lib/ast/expressions/type-computers). +Additionally, in [`operator-registry.ts`](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/lib/ast/expressions/operator-registry.ts), a type computer is registered for each kind of operator. + +In case the algorithm fails to infer a type, e.g. due to unsupported operand types or unresolved references in literals, the resulting type is `undefined`. +In order to report diagnostics in such cases, a `ValidationContext` object can be supplied when calling the type inference. + +## Evaluation of expressions + +The evaluation has the purpose of computing a single value out of an expression. +For a successful evaluation, it is a **precondition** that **a type could successfully be inferred** from the respective expression. +Also, the **value for each free variable** in that expression (i.e. literals resembling placeholders for values) needs to be **known beforehand**. +Therefore, an `ExecutionContext` object is passed to the evaluation which holds values for free variables in the current context. + +### Algorithm + +The algorithm for evaluating expressions is also based on a recursive depth-first search, similar to how the type inference works: + +Again, literals are the base case. A value literal trivially evaluates to its own value. +Other literals that resemble free variables evaluate to their value provided by the given `ExecutionContext` object. + +Regarding operators, their operands first are evaluated recursively. +Next, using the concrete operand values, the operator computes its resulting value. +This is defined in operator evaluator classes located [here](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/lib/ast/expressions/evaluators). +The classes are registered in [operator-registry.ts](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/lib/ast/expressions/operator-registry.ts) for every operator, similar to the previously mentioned type computers. + +The result of an evaluation may be `undefined` in case any errors occurred. +If the preconditions were all met, such errors are most likely arithmetic errors (like division by zero). +It is possible to provide a `ValidationContext` object to the evaluation for reporting such errors as diagnostics. + +### Evaluation strategies + +There are different evaluation strategies to choose from. +They affect the way, expressions are evaluated and thus have an impact on their semantics: + +- `exhaustive`: A full depth-first search is performed, all parts of an expression are evaluated. +- `lazy`: An evaluation with the least effort is performed. Logical operators use [short circuit semantics](https://en.wikipedia.org/wiki/Short-circuit_evaluation) and binary operators don't evaluate their right operand if the left one evaluated to `undefined`. diff --git a/apps/docs/docs/dev/04-guides/04-expressions-and-operators.md.license b/apps/docs/docs/dev/04-guides/04-expressions-and-operators.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/docs/dev/04-guides/04-expressions-and-operators.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/docs/dev/04-guides/05-standard-library.md b/apps/docs/docs/dev/04-guides/05-standard-library.md new file mode 100644 index 00000000..2da1646f --- /dev/null +++ b/apps/docs/docs/dev/04-guides/05-standard-library.md @@ -0,0 +1,48 @@ +--- +title: Working with the Standard Library +sidebar_position: 5 +--- + +Jayvee ships with its own standard library on board, including the most often used _value types_, transformations, and so on. +The standard library itself is written in `.jv` files [here](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/stdlib/). + +## Standard Library Contents + +The following elements are part of the standard library: + +## Builtin Contents + +The implementations of built-in contents are not expressed in Jayvee itself but on the TypeScript layer. Examples: + +- **Builtin value types**: These _value types_ are the base for defining user-defined _value types_ in Jayvee, e.g., `text`, `integer`, `decimal`, `boolean`. +- **Builtin io types**: These _io types_ are used to describe in inputs and outputs of _block types_, e.g., `Sheet`, `File`. +- **Builtin block types**: These _block types_ are the very basic building _blocks_ in Jayvee, e.g., `HttpExtractor`, `SqliteLoader`. +- **Builtin constraint types**: These _constraint types_ are constraints with custom logic, e.g., `LengthConstraint`, `RegexConstraint`. + +Builtin definitions are usually generated and added to the standard library from the internal representations of the concepts. + +### User-defined Contents + +The implementations of user-defined contents are expressed in Jayvee itself. Examples: + +- **User-defined value types**: These _value types_ are based on built-in or other user-defined _value types_. Their definition is expressed natively in Jayvee, e.g., `Percent`. +- **User-defined block types**: These _block types_ are based on built-in or other user-defined _block types_. Their definition is expressed natively in Jayvee. + +We use `jv` files to add user-defined _value types_ to the standard library (see below). + +## Extending the Standard Library + +Just add `jv` files to the directory [here](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/stdlib/). It is crawled hierarchically, meaning that you can also organize files in folders. + +## Implementation + +### 1. Code generation + +We use code generation to transform these `.jv` files into TypeScript files that the language server can used. The [generation script](https://github.com/jvalue/jayvee/tree/main/tools/scripts/language-server/generate-stdlib.mjs) is run via `npm run generate` next to the AST generation. + +### 2. Builtin libraries + +The solution we chose to implement the standard library mechanism is close to the [built-in library tutorial](https://langium.org/guides/builtin-library/) by Langium. The following components are of interest: + +- [JayveeWorkspaceManager](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/lib/builtin-library/jayvee-workspace-manager.ts) in the `language-server` that registers all libraries with the langium framework. +- [StandardLibraryFileSystemProvider](https://github.com/jvalue/jayvee/tree/main/apps/vs-code-extension/src/standard-library-file-system-provider.ts) in the `vs-code-extension` that registers all libraries with the vscode plugin framework. diff --git a/apps/docs/docs/dev/04-guides/05-standard-library.md.license b/apps/docs/docs/dev/04-guides/05-standard-library.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/docs/dev/04-guides/05-standard-library.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/docs/dev/04-guides/06-jayvee-extensions.md b/apps/docs/docs/dev/04-guides/06-jayvee-extensions.md new file mode 100644 index 00000000..e10d2431 --- /dev/null +++ b/apps/docs/docs/dev/04-guides/06-jayvee-extensions.md @@ -0,0 +1,190 @@ +--- +title: Jayvee Extensions +sidebar_position: 6 +--- + +## Concepts + +### Jayvee extension + +A Jayvee extension is used to add new block types to the language without having to modify the actual grammar of the language. Such a Jayvee extension usually consists of two parts: a language extension and an execution extension which are described in the upcoming two sections. + +Jayvee extensions that shall be used by default are bundled into the so-called [standard extension](https://github.com/jvalue/jayvee/tree/main/libs/extensions/std). That way, the [language server](https://github.com/jvalue/jayvee/tree/main/libs/language-server) and the [interpreter](https://github.com/jvalue/jayvee/tree/main/apps/interpreter) are able to load them out of the box. + +### Language extension + +A language extension defines meta information of block types which are required by the +[language server](https://github.com/jvalue/jayvee/tree/main/libs/language-server). +Such meta information describes properties of +block types such as their names, input / output types and their properties. + +Note that language extensions don't define any behavior. Instead, this is done by the corresponding execution extension. + +### Execution extension + +An execution extension defines behavior for block types. They build on the meta information from the corresponding +language extension, e.g. input / output types of the block need to match the signature of the execution method and +properties are accessed by their specified name. + +Execution extensions are only required by the [interpreter](https://github.com/jvalue/jayvee/tree/main/apps/interpreter) and not necessarily by the [language server](https://github.com/jvalue/jayvee/tree/main/libs/language-server) as they solely define behavior. + +## Recipes + +### Add a new Jayvee execution extension + +#### 1. Generate an execution libraries + +```bash +npx nx g @nx/node:library --name="extensions/<extension-name>/exec" +``` + +#### 2. Create extension classes + +In `libs/extensions/<extension-name>/exec/src/extension.ts`: + +```typescript +import { + BlockExecutorClass, + JayveeExecExtension, +} from '@jvalue/jayvee-execution'; + +export class MyExecExtension extends JayveeExecExtension { + getBlockExecutors(): BlockExecutorClass[] { + return []; + } +} +``` + +#### 3. Export extension classes + +In `libs/extensions/<extension-name>/exec/src/index.ts`: + +```typescript +export * from './extension'; +``` + +#### 4. Register new extension classes in the standard extension + +In `libs/extensions/std/exec/src/extension.ts`: + +```typescript +// ... + +import { MyExecExtension } from '@jvalue/jayvee-extensions/<extension-name>/exec'; + +export class StdExecExtension extends JayveeExecExtension { + private readonly wrappedExtensions: JayveeExecExtension[] = [ + // ... + // Register your execution extension here: + new MyExecExtension(), + // ... + ]; + + // ... +} +``` + +### Add a new block type in a Jayvee extension + +#### 1. Create a built-in block type + +Define the syntax of the new _block type_ in the [language server's built-in _block types_](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/stdlib/builtin-blocktypes). + +The following example defines a block type `MyExtractor` with a text property called `url` and a property `retries` with a default value: + +```jayvee +builtin blocktype MyExtractor { + input default oftype None; + output default oftype Sheet; + + property url oftype text; + property retries oftype interger: 10; +} +``` + +The new block type will be automatically registered on the language server startup. + +#### 2. Add custom validation logic (if required) + +If the block type and/or its properties requires custom validation logic, you can implement it in the [language server's block type specific checks](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/lib/validation/checks/blocktype-specific). + +#### 3. Implement `BlockExecutor` + +The following example implements an executor for the previously defined block type `MyExtractor`. + +The `execute` method defines the behavior when a block is executed. Its signature matches the input and output types defined in `MyExtractor.jv` file. + +In `libs/extensions/<extension-name>/exec/src/lib/my-extractor-executor.ts`: + +```typescript +import * as R from '@jvalue/jayvee-execution'; +import { + AbstractBlockExecutor, + BlockExecutorClass, + ExecutionContext, + Sheet, + implementsStatic, +} from '@jvalue/jayvee-execution'; +import { IOType, PrimitiveValuetypes } from '@jvalue/jayvee-language-server'; + +@implementsStatic<BlockExecutorClass>() +export class MyExtractorExecutor + extends AbstractBlockExecutor<IOType.NONE, IOType.SHEET> +{ + // Needs to match the type in meta information: + public static readonly type = 'MyExtractor'; + + public readonly inputType = IOType.NONE; + public readonly outputType = IOType.SHEET; + + async doExecute( + input: None, + context: ExecutionContext, + ): Promise<R.Result<Sheet>> { + // Accessing property values by their name: + const url = context.getPropertyValue( + 'url', + PrimitiveValuetypes.Text, + ); + const limit = context.getPropertyValue( + 'limit', + PrimitiveValuetypes.Integer, + ); + + // ... + + if (error) { + return R.err(...); + } + + return R.ok(...); + } +} +``` + +> **Info** +> The interface `BlockExecutor<I,O>` is used as an API for block executors. The abstract class `AbstractBlockExecutor<I,O>` gives some further functionality for free, e.g., debug logging. + +> **Warning** +> The generic types of `AbstractBlockExecutor<I,O>` need to match the input and output types of the corresponding _block type_ definition. + +#### 4. Register the new `BlockExecutor` in the execution extension + +In `libs/extensions/<extension-name>/exec/src/extension.ts`: + +```typescript +// ... + +import { MyExtractorExecutor } from './lib/my-extractor-executor'; + +export class MyExecExtension extends JayveeExecExtension { + getBlockExecutors(): BlockExecutorClass[] { + return [ + // ... + // Register your block executor here: + MyExtractorExecutor, + // ... + ]; + } +} +``` diff --git a/apps/docs/docs/dev/04-guides/06-jayvee-extensions.md.license b/apps/docs/docs/dev/04-guides/06-jayvee-extensions.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/docs/dev/04-guides/06-jayvee-extensions.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/docs/dev/04-guides/_category_.json b/apps/docs/docs/dev/04-guides/_category_.json new file mode 100644 index 00000000..80f6fce8 --- /dev/null +++ b/apps/docs/docs/dev/04-guides/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Guides", + "position": 4, + "link": { + "type": "generated-index", + "description": "Here you can find guides that will help you developing certain aspects of Jayvee." + } +} diff --git a/apps/docs/docs/dev/04-guides/_category_.json.license b/apps/docs/docs/dev/04-guides/_category_.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/docs/dev/04-guides/_category_.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/docs/dev/05-design-principles.md b/apps/docs/docs/dev/05-design-principles.md new file mode 100644 index 00000000..8b15dd8c --- /dev/null +++ b/apps/docs/docs/dev/05-design-principles.md @@ -0,0 +1,24 @@ +--- +title: Design Principles +sidebar_position: 5 +--- + +When deciding on new features for the domain-specific language itself, we try to adhere to the following high level guidelines. Of course, none of these statements is set in stone and every decision is a tradeoff. + +## Jayvee Manifesto +_Inspired by the [Agile Manifesto](https://agilemanifesto.org/)._ + +We are uncovering better ways of _modeling data pipelines by providing a domain-specific language for data engineering and making it easy for everyone to participate in it_. + +Through this work we have come to value: + +1. **Describing a goal state** over how to get there. +2. **Explicit modeling** over hidden magic. +3. **Composition** over inheritance. +4. **Flat structures** over deep nesting. + +That is, while there is value in the items on the right, we value the items on the left more. + +Through this work, we also have come to explore: + +5. **Libraries** over language features. \ No newline at end of file diff --git a/apps/docs/docs/dev/05-design-principles.md.license b/apps/docs/docs/dev/05-design-principles.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/docs/dev/05-design-principles.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/docs/dev/12-jayvee-testing.md b/apps/docs/docs/dev/12-jayvee-testing.md new file mode 100644 index 00000000..47b086ed --- /dev/null +++ b/apps/docs/docs/dev/12-jayvee-testing.md @@ -0,0 +1,259 @@ +--- +title: Writing tests for Jayvee +--- + +In order to ensure that Jayvee works as intended and to catch breaking changes, we have implemented the following components for regression testing: + +- Testing utils: utils to create Langium Typescript objects from \*.jv test assets (see [here](#testing-utils)) as well as mocks for execution testing (see [here](#testing-utils-1)) +- [Grammar tests](#grammar-tests): test the grammar parsing and validation +- [Execution tests](#execution-tests): test the execution of blocks + +## Conventions + +All of the existing tests follow these conventions: + +1. The `<file-name>.spec.ts` file is located next to the `<file-name>.ts` file itself. +2. The `*.jv` assets are located inside a `test/assets/<file-name>` folder. + Take a look at one of the exisiting tests for more details. + +## Grammar tests + +These kind of tests are mainly located inside the [language-server](https://github.com/jvalue/jayvee/tree/main/libs/language-server) as well as the language parts of each extension (for example [std/lang](https://github.com/jvalue/jayvee/tree/main/libs/extensions/std/lang)). + +### Testing utils + +The testing utils are located inside the `language-server` in a dedicated [test folder](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/test). +These utils can be imported using `@jvalue/jayvee-language-server/test` and contain the following parts: + +[**langium-utils.ts**](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/test/langium-utils.ts): +This utils file contains two functions: + +- `parseHelper` to simplify parsing the input (content of a \*.jv file) and returning the corresponding `LangiumDocument`, and +- `validationHelper` parse and validate the created document. + They are kept in a separate file due to being copied from the Langium repository and thus subject to a different code license and copyright. + +[**utils.ts**](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/test/utils.ts): +This file contains custom testing utility utils functions, like `readJvTestAssetHelper` for reading jv test assets. +Example: + +```ts +import path from 'node:path'; + +import { createJayveeServices } from '@jvalue/jayvee-language-server'; +import { + ParseHelperOptions, + expectNoParserAndLexerErrors, + parseHelper, + readJvTestAssetHelper, +} from '@jvalue/jayvee-language-server/test'; +import { AstNode, LangiumDocument } from 'langium'; + +describe('My example test', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../test/assets/', // localized path to test assets folder + ); + + beforeAll(() => { + // [...] register extensions etc + const services = createJayveeServices(NodeFileSystem).Jayvee; // Or retrieve them if services already exist + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + // [...] + + it('My dummy test', () => { + const text = readJvTestAsset('<sub-folder>/<test-asset-name>.jv'); + + const document = await parse(text); + expectNoParserAndLexerErrors(document); + // Rest of test + }); +}); +``` + +If you want to simply validate the test assets, simply replace `parseHelper` with `validationHelper` (and adjust the types). +You can find detailed documentation of all the utility functions directly in the code. + +[**extension/**](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/test/extension): +This folder contains a Jayvee extension for testing. +If there are certain blocks required for testing a certain feature, they can be defined here. +One such example is the already defined `TestProperty` block which has a multitude of different properties, each with a different type. +This block is used for testing properties and property-assignments. +The extension provides loader and extractor blocks for all IOTypes without any properties. +These blocks are automatically generated at runtime with the following naming scheme: +`Test${ioType}${io === 'input' ? 'Loader' : 'Extractor'}` (Example: `TestFileExtractor`). +This allows for easy (grammar) testing of non loader/extractor blocks: + +```jv +pipeline Pipeline { + + TestExtractor -> BlockUnderTest -> TestLoader; + + block BlockUnderTest oftype CellWriter { + at: range A1:A3; + write: ['values', 'to', 'write']; + } + + block TestExtractor oftype TestSheetExtractor { } + block TestLoader oftype TestSheetLoader { } +} +``` + +### Existing tests + +Currently there are already tests for the following parts: + +- Language-server validation checks (located [here](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/lib/validation)) +- Language-server constraint validation (located [here](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/lib/constraint)) +- Custom block (property) validation of the three existing extensions (std extension located [here](https://github.com/jvalue/jayvee/blob/main/libs/extensions/std/lang/src)) +- Grammar validation tests for all official full examples from the [/example](https://github.com/jvalue/jayvee/tree/main/example) folder (located [here](https://github.com/jvalue/jayvee/blob/main/libs/extensions/std/lang/src/example-validation.spec.ts)) +- Grammar validation tests for all block examples of the std extension (located [here](https://github.com/jvalue/jayvee/blob/main/libs/extensions/std/lang/src/meta-inf-example-validation.spec.ts)) + +## Execution tests + +These kind of tests are mainly located inside the [interpreter](https://github.com/jvalue/jayvee/tree/main/libs/language-server), the [interpreter-lib](https://github.com/jvalue/jayvee/tree/main/libs/interpreter-lib), the [execution lib](https://github.com/jvalue/jayvee/tree/main/libs/execution) as well as the execution parts of each extension (for example [std/exec](https://github.com/jvalue/jayvee/tree/main/libs/extensions/std/exec)). + +### Testing utils + +The testing utils for execution tests are spread between the extensions, with the interfaces and base utils located inside the [execution lib](https://github.com/jvalue/jayvee/tree/main/libs/execution). +They can be imported using `@jvalue/jayvee-extensions/rdbms/test`, `@jvalue/jayvee-extensions/std/test` and `@jvalue/jayvee-execution/test`. + +[**block-executor-mocks.ts**](https://github.com/jvalue/jayvee/blob/main/libs/execution/test/block-executor-mock.ts): +`BlockExecutorMock` interface for defining mocks for `AbstractBlockExecutor`. Generally only loader and executor blocks require mocks, because they interact with "the outside world" (i.e. `HttpExtractor` making http calls). +Due to how vastly different each `BlockExecutor` can be, this interface is very simple, containing only a `setup(...args: unknown[])` and a `restore()` method. See below for existing implementations. + +[**rdbms/exec/test**](https://github.com/jvalue/jayvee/tree/main/libs/extensions/rdbms/exec/test): +Contains the implementation of `BlockExecutorMock` for `PostgresLoaderExecutor` and `SQLiteLoaderExecutor`. +Both of these executors are mocked using `vi.mock` to mock the corresponding libraries (`pg` and `sqlite3`) +**Usage:** + +```ts +import { + PostgresLoaderExecutorMock, + SQLiteLoaderExecutorMock, +} from '@jvalue/jayvee-extensions/rdbms/test'; + +// Global mocking of external library at the top of test file required, +// even though the mocking is encapsulated in helper classes +vi.mock('pg', () => { + const mClient = { + connect: vi.fn(), + query: vi.fn(), + end: vi.fn(), + }; + return { + default: { + Client: vi.fn(() => mClient), + }, + }; +}); +vi.mock('sqlite3', () => { + const mockDB = { + close: vi.fn(), + run: vi.fn(), + }; + return { + default: { + Database: vi.fn(() => mockDB), + }, + }; +}); + +describe('Dummy describe', () => { + // [...] + + let postgresLoaderMock: PostgresLoaderExecutorMock; + let sqliteLoaderMock: SQLiteLoaderExecutorMock; + + beforeAll(() => { + postgresLoaderMock = new PostgresLoaderExecutorMock(); + sqliteLoaderMock = new SQLiteLoaderExecutorMock(); + }); + + afterEach(() => { + postgresLoaderMock.restore(); + sqliteLoaderMock.restore(); + }); + + it('Dummy test', async () => { + // Prepare mocks + postgresLoaderMock.setup(); + sqliteLoaderMock.setup(); + + // [...] execute test + + expect(postgresLoaderMock.pgClient.connect).toBeCalledTimes(1); + expect(postgresLoaderMock.pgClient.query).toBeCalledTimes(1); + expect(postgresLoaderMock.pgClient.end).toBeCalledTimes(1); + expect(sqliteLoaderMock.sqliteClient.run).toBeCalledTimes(1); + expect(sqliteLoaderMock.sqliteClient.close).toBeCalledTimes(1); + }); +}); +``` + +[**std/exec/test/mocks**](https://github.com/jvalue/jayvee/tree/main/libs/extensions/std/exec/test): +Contains the implementation of `BlockExecutorMock` for `HttpExtractorExecutorMock`. +This implementation uses [nock](https://www.npmjs.com/package/nock) for mocking HTTP(S) responses. +The `setup` method is further specified requiring one parameter `registerMocks: () => Array<nock.Scope>`, which returns all used `nock.Scope` (i.e. the return value of `nock('<URL>')`), see usage below: +**Usage:** + +```ts +import path from 'node:path'; + +import { HttpExtractorExecutorMock } from '@jvalue/jayvee-extensions/std/test'; + +describe('Dummy describe', () => { + // [...] + + let httpExtractorMock: HttpExtractorExecutorMock; + + beforeAll(() => { + httpExtractorMock = new HttpExtractorExecutorMock(); + }); + + afterEach(() => { + httpExtractorMock.restore(); + }); + + it('should have no errors when executing gtfs-static-and-rt.jv example', async () => { + // Prepare mocks + httpExtractorMock.setup(() => { + return [ + nock('<URL_1>') + .get('<PATH>') + .replyWithFile( + 200, + path.resolve(__dirname, '../test/assets/file1.zip'), + { + 'Content-Type': 'application/octet-stream', + }, + ), + nock('<URL_2>') + .get('<PATH_1>') + .replyWithFile(200, path.resolve(__dirname, '../test/assets/file2'), { + 'Content-Type': 'application/octet-stream', + }) + .get('<PATH_2>') + .reply(200, { content: 'My dummy json reply.' }), + ]; + }); + + // [...] execute test + + expect(httpExtractorMock.nockScopes.every((scope) => scope.isDone())); + }); +}); +``` + +### Existing tests + +Currently there are already tests for the following parts: + +- Smoke test for official examples (located [here](https://github.com/jvalue/jayvee/blob/main/apps/interpreter/src/examples-smoke-test.spec.ts)) diff --git a/apps/docs/docs/dev/12-jayvee-testing.md.license b/apps/docs/docs/dev/12-jayvee-testing.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/docs/dev/12-jayvee-testing.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/docs/user/.gitignore b/apps/docs/docs/user/.gitignore new file mode 100644 index 00000000..d1b2321d --- /dev/null +++ b/apps/docs/docs/user/.gitignore @@ -0,0 +1,3 @@ +# SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +# +# SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/docs/user/block-types/.gitignore b/apps/docs/docs/user/block-types/.gitignore new file mode 100644 index 00000000..277e9bb1 --- /dev/null +++ b/apps/docs/docs/user/block-types/.gitignore @@ -0,0 +1,5 @@ +# SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +# +# SPDX-License-Identifier: AGPL-3.0-only + +*.md \ No newline at end of file diff --git a/apps/docs/docs/user/block-types/_category_.json b/apps/docs/docs/user/block-types/_category_.json new file mode 100644 index 00000000..63f8d066 --- /dev/null +++ b/apps/docs/docs/user/block-types/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Block Types", + "position": 3, + "link": { + "type": "generated-index", + "description": "These block types are shipped with Jayvee and are available right out of the box." + } +} diff --git a/apps/docs/docs/user/block-types/_category_.json.license b/apps/docs/docs/user/block-types/_category_.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/docs/user/block-types/_category_.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/docs/user/composite-block-types.md b/apps/docs/docs/user/composite-block-types.md new file mode 100644 index 00000000..e0e9df15 --- /dev/null +++ b/apps/docs/docs/user/composite-block-types.md @@ -0,0 +1,118 @@ +--- +sidebar_position: 4 +--- + +# Composite Block Types + +_Composite block types_ are a way to create new _block types_ in Jayvee by combining the functionality of existing _blocks_ and _pipes_. By relying on _composite block types_ instead of implementing more _built-in block types_ in a language interpreter, Jayvee supports easy extension by users. + +_Composite block types_ define: + +- with the `property` keyword: properties with a name and [value type](<./core-concepts.md#value types>), optionally a default value +- with the `input` keyword: one input with a name and _io type_ (that can be `None`) +- with the `output` keyword: one output with a name and _io type_ (that can be `None`) +- one _pipeline_ definition, starting from the input (using its name) and ending in the output (again using its name) +- all _blocks_ that are used in the _pipeline_ definition (either _blocks_ of _built-in_ or _composite block types_) + +## Example + +As an example, the common use-case of extracting a CSV file from a web server using HTTP. With _built-in block types_, a _pipeline_ would start with a `HttpExtractor` source that downloads a file from the internet and outputs a binary file. This file must be interpreted as text (using a `TextFileInterpreter`) and finally as `Sheet` (using a `CSVInterpreter`). + +### Implementation with built-in block types + +```mermaid +flowchart LR + A[HttpExtractor] --> B(TextFileInterpreter) + B --> C(CSVInterpreter) + C --> D(TableInterpreter) + D --> E[SQLiteSink] +``` + +A _pipeline_ with _blocks_ using _built-in block types_ is very verbose: + +```jayvee +pipeline CarsPipeline { + CarsExtractor + -> CarsTextFileInterpreter + -> CarsCSVInterpreter + -> CarsTableInterpreter + -> CarsLoader; + + block CarsExtractor oftype HttpExtractor { + url: "https://example.com/cars.csv"; + } + + block CarsTextFileInterpreter oftype TextFileInterpreter { } + + block CarsCSVInterpreter oftype CSVInterpreter { + enclosing: '"'; + } + // ... further block definitions +} +``` + +### Refactoring using composite block types + +The common use-case of downloading a CSV file using HTTP can be refactored into a _composite block type_. Note that we define all properties of the _built-in blocks_ that are used as properties of the new `CSVExtractor` _block type_ (but add fallback values). If some internal configuration is always the same, we could also not expose it as a property of the new _block type_. + +```jayvee +// Define a new composite block type named CSVExtractor outside of the pipeline +composite blocktype CSVExtractor { + // Properties of the CSVExtractor, some with default values + property url oftype text; + property delimiter oftype text: ','; + property enclosing oftype text: ''; + property enclosingEscape oftype text: ''; + + // Input and outputs + input inputName oftype None; + output outputName oftype Sheet; + + // Pipeline definition from input, over blocks defined later, to output + inputName + ->FileExtractor + ->FileTextInterpreter + ->FileCSVInterpreter + ->outputName; + + // Block definitions using values from properties by name + block FileExtractor oftype HttpExtractor { url: url; } + block FileTextInterpreter oftype TextFileInterpreter {} + + block FileCSVInterpreter oftype CSVInterpreter { + delimiter: delimiter; + enclosing: enclosing; + enclosingEscape: enclosingEscape; + } +} +``` + +With the new `CSVExtractor` _composite block type_, the _pipeline_ now looks like this. + +```mermaid +flowchart LR + CSVExtractor --> D(TableInterpreter) + D --> E[SQLiteSink] + + subgraph CSVExtractor + A[HttpExtractor] --> B(TextFileInterpreter) + B --> C(CSVInterpreter) +end +``` + +If the `CSVExtractor` is available in the scope of the `CarsPipeline` from before (e.g., by defining it above the _pipeline_), it can then be used to shorten the actual _pipeline_ code. + +```jayvee +pipeline CarsPipeline { + // HttpExtractor, TextFileInterpreter and CSVInterpreter have been replaced by CSVExtractor + CarsExtractor + -> CarsTableInterpreter + -> CarsLoader; + + block CarsExtractor oftype CSVExtractor { + url: "https://example.com/cars.csv"; + } + + // ... further block definitions +} +``` diff --git a/apps/docs/docs/user/composite-block-types.md.license b/apps/docs/docs/user/composite-block-types.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/docs/user/composite-block-types.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/docs/user/constraint-types/.gitignore b/apps/docs/docs/user/constraint-types/.gitignore new file mode 100644 index 00000000..277e9bb1 --- /dev/null +++ b/apps/docs/docs/user/constraint-types/.gitignore @@ -0,0 +1,5 @@ +# SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +# +# SPDX-License-Identifier: AGPL-3.0-only + +*.md \ No newline at end of file diff --git a/apps/docs/docs/user/constraint-types/_category_.json b/apps/docs/docs/user/constraint-types/_category_.json new file mode 100644 index 00000000..6cb00cc8 --- /dev/null +++ b/apps/docs/docs/user/constraint-types/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Constraint Types", + "position": 6, + "link": { + "type": "generated-index", + "description": "These constraint types are shipped with Jayvee and are available right out of the box." + } +} diff --git a/apps/docs/docs/user/constraint-types/_category_.json.license b/apps/docs/docs/user/constraint-types/_category_.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/docs/user/constraint-types/_category_.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/docs/user/core-concepts.md b/apps/docs/docs/user/core-concepts.md new file mode 100644 index 00000000..4c8cf91a --- /dev/null +++ b/apps/docs/docs/user/core-concepts.md @@ -0,0 +1,89 @@ +--- +sidebar_position: 2 +--- + +# Core Concepts + +The core concepts of Jayvee are _pipelines_, _blocks_, and _value types_. + +## Pipelines + +A _pipeline_ is a sequence of different computing steps, the _blocks_. +The default output of a _block_ becomes the default input of the next _block_, building a chain of computing steps. +In the scope of a _pipeline_, you can connect these _blocks_ via the _pipe_ syntax: + +```jayvee +pipeline CarsPipeline { + // Assumption: blocks "GasReserveHttpExtractor", "GasReserveCSVInterpreter", "GasReserveTableInterpreter", and "GasReserveLoader" are defined + + GasReserveHttpExtractor + -> GasReserveTextFileInterpreter + -> GasReserveCSVInterpreter + -> GasReserveTableInterpreter + -> GasReserveLoader; +} +``` + +## Blocks + +A _block_ is a processing step within a _pipeline_. +It can have a default input and a default output. +We differentiate the following types of _blocks_: +- _Extractor blocks_ do not have a default input but only a default output. They model a **data source**. +- _Transformator blocks_ have a default input and a default output. They model a **transformation**. +- _Loader blocks_ do have a default input but nor a default output. They model a **data sink**. + +The general structure of a _pipeline_ consisting of different blocks is the following: + +```mermaid +flowchart LR + A[ExtractorBlock] --> B(TransformatorBlock) + B --> C(TransformatorBlock) + C --> D(LoaderBlock) +``` + +The common syntax of _blocks_ is at its core a key-value map to provide configuration to the _block_. +The availability of property keys and their respective _value types_ is determined by the type of the _block_, called _block type_ - indicated by the identifier after the keyword `oftype`: + +```jayvee +block GasReserveHttpExtractor oftype HttpExtractor { + // key: value + url: "https://www.bundesnetzagentur.de/_tools/SVG/js2/_functions/csv_export.html?view=renderCSV&id=1089590"; +} +``` + +In the example above, the `url` property of type `text` is defined by the corresponding `HttpExtractor` _block type_. + +_Blocks_ can be either defined as part of the language, called _built-in_ or defined as composition of existing _blocks_ by users in Jayvee, called _composite block types_. See the documentation for [_composite block types_](./composite-block-types.md). + +## Value types + +A _value type_ is the definition of a data type of the processed data. +Some _blocks_ use _value types_ to define logic (like filtering or assessing the data type in a data sink). +We differentiate the following kinds of _value types_: +- _Built-in value types_ come with the basic version of Jayvee. See [built-in value types](./value-types/built-in-value-types). +- _Primitive value types_ can be defined by the user to model domain-specific data types and represent a single value. + _Constraints_ can be added to a _primitive value types_. +See [primitive value types](./value-types/primitive-value-types). +- _Compound value types_: UPCOMING. + +```jayvee +valuetype GasFillLevel oftype integer { + constraints: [ GasFillLevelRange ]; +} + +constraint GasFillLevelRange on decimal: + value >= 0 and value <= 100; +``` + +## Transforms +_Transforms_ are used to transform data from one _value type_ to a different one. For more details, see [transforms](./transforms.md). + +```jayvee +transform CelsiusToKelvin { + from tempCelsius oftype decimal; + to tempKelvin oftype decimal; + + tempKelvin: tempCelsius + 273.15; +} +``` \ No newline at end of file diff --git a/apps/docs/docs/user/core-concepts.md.license b/apps/docs/docs/user/core-concepts.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/docs/user/core-concepts.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/docs/user/examples/.gitignore b/apps/docs/docs/user/examples/.gitignore new file mode 100644 index 00000000..4a27c98c --- /dev/null +++ b/apps/docs/docs/user/examples/.gitignore @@ -0,0 +1,6 @@ +# SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +# +# SPDX-License-Identifier: AGPL-3.0-only + +!README.mdx +*.md \ No newline at end of file diff --git a/apps/docs/docs/user/examples/README.mdx b/apps/docs/docs/user/examples/README.mdx new file mode 100644 index 00000000..3b8b9448 --- /dev/null +++ b/apps/docs/docs/user/examples/README.mdx @@ -0,0 +1,15 @@ +--- +sidebar_position: 10 +--- + +# Jayvee Examples + +Examples of Jayvee models. +Copy them to your local file system and execute them with the `jv` command on your command line (see [usage](/docs/user/intro#usage)). +You can [find all examples on Github](https://github.com/jvalue/jayvee/tree/main/example). + +```mdx-code-block +import DocCardList from '@theme/DocCardList'; + +<DocCardList /> +``` diff --git a/apps/docs/docs/user/examples/README.mdx.license b/apps/docs/docs/user/examples/README.mdx.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/docs/user/examples/README.mdx.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/docs/user/expressions.md b/apps/docs/docs/user/expressions.md new file mode 100644 index 00000000..a2cceb49 --- /dev/null +++ b/apps/docs/docs/user/expressions.md @@ -0,0 +1,87 @@ +--- +sidebar_position: 7 +--- + +# Expressions + +Expressions in Jayvee are arbitrarily nested statements. They consist of: +- literals (e.g., numbers `5` or strings `"Example"`) +- variables (e.g., declared by `from` properties in [Transforms](./transforms.md)) +- operators (e.g., `*` or `sqrt`) + +Expressions get evaluated at runtime by the interpreter to a [built-in _value type_](./value-types/built-in-value-types). + +### Example + +The following expression is evaluated to the `integer` `10`: `(2 + 3) * 2` + +The following expression is evaluated to the `boolean` `true`: `"Example" == "Example"` + +The following expression is evaluated to the `text` `I love Datypus`: `"I love platypuses" replace /platypuses/ with "Datypus"` + +### List of Operators + +#### Arithmetics (binary operators) +- `+` for addition, e.g., `5 + 3` evaluates to `8` +- `-` for subtraction, e.g., `5 - 3` evaluates to `2` +- `*` for multiplication, e.g., `5 * 3` evaluates to `15` +- `/` for division, e.g., `6 / 3` evaluates to `2` +- `%` for modulo, e.g., `5 % 3` evaluates to `2` +- `pow` for power, e.g., `2 pow 3` evaluates to `8` +- `root` for root, e.g., `27 root 3` evaluates to `3` + +#### Arithmetics (unary operators) +- `+` for positive signing, e.g., `+5` evaluates to `5` +- `-` for negative signing, e.g., `-5` evaluates to `-5` +- `sqrt` for square root, e.g., `sqrt 9` evaluates to `3` +- `foor` for flooring a number, e.g., `floor 5.3` evaluates to `5` +- `ceil` for ceiling a number, e.g., `floor 5.3` evaluates to `6` +- `round` for rounding a number, e.g., `floor 5.3` evaluates to `5` + +#### Relational (binary operators) +- `<` for smaller, e.g., `3 < 3` evaluates to `false` +- `<=` for smaller or equal, e.g., `3 <= 3` evaluates to `true` +- `>` for greater, e.g., `3 > 3` evaluates to `false` +- `>=` for greater or equal, e.g., `3 >= 3` evaluates to `true` +- `==` for equal, e.g., `3 == 3` evaluates to `true` +- `!=` for not equal, e.g., `3 != 3` evaluates to `false` + +#### Logical (binary operators) +- `and` for a logical and (both need to be true to evaluate to true) +- `or` for a logical or (at least left or right needs to be true to evaluate to true) +- `xor` for a logical xor (either left or right needs to be true to evaluate to true) + +#### Logical (unary operators) +- `not` for logical negation, `not true` evaluates to `false` + +#### Others (binary operators) +- `matches` for a regex match, e.g., `"A07" matches /^[A-Z0-9]*$/` evaluates to `true` +- `in` for inclusion in an array, e.g., `"a" in ["a", "b", "c"]` evaluates to `true` + +#### Text manipulation (unary operators) +- `lowercase` converts all alphabetic characters in a text to lowercase +- `uppercase` converts all alphabetic characters in a text to uppercase + +#### Text manipulation (ternary operators) +- `replace [...] with [...]` replaces regex matches in a text with a string + +### Operator Details + +#### `in` Operator + +The `in` operator checks whether a value is included in a collection of values. For example: + +```jayvee +4.5 in [3, 6.5] // evaluates to false +3 in [3.0, 6.5] // evaluates to true +"a" in ["a", "b", "c"] // evaluates to true +``` + +The operator supports `text`, `integer` and `decimal` values as operands. The compatibility of left and right operand types follows these rules: +- For the `in` operator we have a type for the needle (left operand) and a type for the elements in the haystack (right operand). +- There is an automated type conversion as long as it is lossless and clearly defined (integer to decimal as of now). +- We allow any combination of operands that has either: (i) An automated type conversion from needle type (left operand) to the type of the elements in the haystack (right operand), or (ii) the other way around. + + +### Further reading +For a deeper documentation of how expressions and operators work internally, refer to the [developer docs](../dev/04-guides/04-expressions-and-operators.md). diff --git a/apps/docs/docs/user/expressions.md.license b/apps/docs/docs/user/expressions.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/docs/user/expressions.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/docs/user/intro/_category_.json b/apps/docs/docs/user/intro/_category_.json new file mode 100644 index 00000000..9da536e2 --- /dev/null +++ b/apps/docs/docs/user/intro/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Introduction to Jayvee", + "position": 1, + "link": { + "type": "generated-index", + "description": "All the essential information to get started with Jayvee." + } +} diff --git a/apps/docs/docs/user/intro/_category_.json.license b/apps/docs/docs/user/intro/_category_.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/docs/user/intro/_category_.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/docs/user/intro/intro.mdx b/apps/docs/docs/user/intro/intro.mdx new file mode 100644 index 00000000..7cd5a401 --- /dev/null +++ b/apps/docs/docs/user/intro/intro.mdx @@ -0,0 +1,108 @@ +--- +sidebar_position: 1 +title: Getting Started +--- + +# Introduction to Jayvee + +import MascotImageUrl from '@site/static/img/mascots/mascot2.png'; + +<div style={{ display: 'flex', flexDirection: 'row', flexWrap: 'none', alignItems: 'center'}}> + <div> + <img src={MascotImageUrl} width="400px" style={{ float: 'left' }} /> + </div> + + <span className={"text--center"}> + <h2>"Making data engineering easy, reliable, and safe"</h2> + </span> +</div> + +Jayvee is a domain-specific language (DSL) for automated processing of data pipelines. +The Jayvee interpreter allows executing such data pipelines on local machines. +Data engineers can use Jayvee and its interpreter to clean and preprocess data for later activities like data science or machine learning. + +## Installation + +Install the interpreter via `npm`. You will need a **nodejs version >= 17.0.0**. + +```bash +npm install -g @jvalue/jayvee-interpreter +``` + +You can install a specific version using the `@`-syntax, e.g., version `0.0.17`: + +```bash +npm install -g @jvalue/jayvee-interpreter@0.0.17 +``` + +## Update + +Details about how to update Jayvee and the VSCode extension can be found [here](./update.md). + +## Usage + +### Show help + +```console +jv -h +``` + +### Run a `.jv` file + +```console +jv <file> +``` + +Run with **additional debug output**: + +```console +jv <file> -d +``` + +With **runtime parameters**: + +```console +jv -e <param>=<value> -e <param>=<value> ... <file> +``` + +### Debug a `.jv` file + +Print debugging is further configured by the parameters `--debug-granularity` and `--debug-target`. + +```console +jv <file> -d -dg peek +``` + +The value of the parameter `--debug-granularity` (short `-dg`) can have the following values: + +- `peek` to log a short summary, including a small subset of data +- `exhaustive` to log a summary, including the full data +- `minimal` to log a summary, including no additional data (default). + To see logs, debugging has to be enabled using the `-d` flag. + +```console +jv <file> -d --debug-granularity peek +``` + +The parameter `--debug-target` (short `-dt`) allows to specify which blocks should be logged for debugging. Separate block names by comma if multiple blocks are targeted. All blocks are logged if the parameter is omitted. + +```console +jv <file> -d --debug-granularity peek --debug-target MyExtractorBlock,MySinkBlock +``` + +## Examples + +You can find multiple examples with inline explanations [here](../examples/README.mdx). You can copy them to your local file system and execute them with the `jv` command on your command line (see [usage](#usage)). + +## VSCode Plugin + +To set up Jayvee locally in VS Code, you need to install the latest Jayvee VS Code extension. +To install the most recent extension, go to our [latest release](https://github.com/jvalue/jayvee/releases/latest) +and download the `jayvee.vsix` file from the release assets. +Next, go to [this page](https://code.visualstudio.com/docs/editor/extension-marketplace#_install-from-a-vsix) and +follow the instructions for installing the downloaded extension. + +## Troubleshooting + +1. Error `structuredClone is not defined` + - Please make sure you use node version 17+. diff --git a/apps/docs/docs/user/intro/intro.mdx.license b/apps/docs/docs/user/intro/intro.mdx.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/docs/user/intro/intro.mdx.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/docs/user/intro/update.md b/apps/docs/docs/user/intro/update.md new file mode 100644 index 00000000..3dd746c9 --- /dev/null +++ b/apps/docs/docs/user/intro/update.md @@ -0,0 +1,52 @@ +--- +sidebar_position: 2 +title: Update Jayvee +--- + +# How to update Jayvee + +Jayvee is consistently getting updates. To ensure you use the most recent version, please regularly update the interpreter and VSCode extension. + +### Update Jayvee + Extension + +To update Jayvee, you need to: + +1. Update the interpreter by reinstalling it using npm: + +```bash +npm install -g @jvalue/jayvee-interpreter +``` + +Note: Updating to a specific version works using the `@`-syntax, e.g., version `0.0.17`: + +```bash +npm install -g @jvalue/jayvee-interpreter@0.0.17 +``` + +2. Update your VSCode Extension: + +- Go [here](https://github.com/jvalue/jayvee/releases/latest) to find the latest(or a specific) version of the VSCode Extenstion. + +- Then, download the latest `jayvee.vsix` file. + +- Finally, to install the extension using the CLI, paste the code below into your command line: + +```bash +code --install-extension jayvee.vsix +``` + +If you'd rather use the manual installation, follow this [link](https://code.visualstudio.com/docs/editor/extension-marketplace#_install-from-a-vsix) for the official VSCode documentation. + +### Version Check + +To verify wether the wanted version of Jayvee and VSCode extension where installed successfully, you can run in your command line: + +For **Jayvee**: + +```bash +jv -V +``` + +For the **VSCode extension**: + +Go to the extensions menu, and look for `Jayvee`. The version is then displayed on the information page of the extension. diff --git a/apps/docs/docs/user/intro/update.md.license b/apps/docs/docs/user/intro/update.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/docs/user/intro/update.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/docs/user/runtime-parameters.md b/apps/docs/docs/user/runtime-parameters.md new file mode 100644 index 00000000..d0620d14 --- /dev/null +++ b/apps/docs/docs/user/runtime-parameters.md @@ -0,0 +1,27 @@ +--- +sidebar_position: 9 +--- + +# Runtime Parameters + +Property values in Jayvee can be assigned to _values_ or left open for later configuration via _runtime parameters_. + +## Syntax + +_Runtime parameters_ are indicated by the `requires` keyword, followed by the identifier of the parameter. Example + +```jayvee +block CarsLoader oftype SQLiteLoader { + table: "Cars"; + file: requires CARS_SQLITE_FILE; +} +``` + +## CLI + +The interpreter CLI has to define all existing runtime parameters for execution. +Use the CLI flag `-e` to to define them as key-value pairs of identifier and value. + +```console +jv -e <param>=<value> -e <param>=<value> ... <file> +``` diff --git a/apps/docs/docs/user/runtime-parameters.md.license b/apps/docs/docs/user/runtime-parameters.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/docs/user/runtime-parameters.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/docs/user/transforms.md b/apps/docs/docs/user/transforms.md new file mode 100644 index 00000000..468a22cb --- /dev/null +++ b/apps/docs/docs/user/transforms.md @@ -0,0 +1,77 @@ +--- +sidebar_position: 8 +--- + +# Transforms + +_Transforms_ are a concept in Jayvee to define the transformation of individual values. +They are similar to functions in programming languages, i.e. they perform computations on some input values and produce output values. _Transforms_ work by mapping input values to outputs using [expressions](./expressions.md). + +:::info Important + +Up to version `0.0.16`, we only supported a single input for transformers! + +::: + +:::info Important + +In its current state, Jayvee only supports a arbitrary numbers of inputs and a single output for transforms. +For the future, it is planned to support arbitrary numbers for outputs as well. + +::: + + +## Syntax + +The general syntax of _transforms_ looks like this: + +```jayvee +transform <name> { + from <inputName> oftype <inputValueType>; + to <outputName> oftype <outputValueType>; + + <outputName>: <expression>; +} +``` + +The `transform` keyword is used to define a _transform_ and give it a name. +The curly braces denote the body of the _transform_. + +The body first contains the definitions of input and output ports. +Input ports are defined using the `from` keyword whereas output ports use the `to` keyword. +Next, they are given a name and, after the `oftype` keyword, typed with a _value type_. + +Below, there needs to be an output assignment for each output port. +The output assignment defines how a particular output value is computed. +They consist of the name of an output port, followed by a `:`. +Next, an [expression](./expressions.md) specifies how the output value shall be computed. +Names of input ports can be used in such an expression to refer to input values. + +### Example + +The following transform converts temperature values from degree Celsius to Kelvin: + +```jayvee +transform CelsiusToKelvin { + from tempCelsius oftype decimal; + to tempKelvin oftype decimal; + + tempKelvin: tempCelsius + 273.15; +} +``` + +The following _transform_ converts a text based status into a boolean value, `true` if the text is `Active`, `false` for any other value: + +```jayvee +transform StatusToBoolean { + from statusText oftype text; + to statusBoolean oftype boolean; + + statusBoolean: statusText == "Active"; +} +``` + +## Applying transforms to table columns + +Transforms can be applied to columns of a table. +Please refer to the documentation of the [`TableTransformer` block type](./block-types/TableTransformer.md) to find out how. diff --git a/apps/docs/docs/user/transforms.md.license b/apps/docs/docs/user/transforms.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/docs/user/transforms.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/docs/user/value-types/.gitignore b/apps/docs/docs/user/value-types/.gitignore new file mode 100644 index 00000000..ba555155 --- /dev/null +++ b/apps/docs/docs/user/value-types/.gitignore @@ -0,0 +1,5 @@ +# SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +# +# SPDX-License-Identifier: AGPL-3.0-only + +built-in-value-types.md \ No newline at end of file diff --git a/apps/docs/docs/user/value-types/_category_.json b/apps/docs/docs/user/value-types/_category_.json new file mode 100644 index 00000000..37375108 --- /dev/null +++ b/apps/docs/docs/user/value-types/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Value Types", + "position": 5, + "link": { + "type": "generated-index", + "description": "Jayvee supports these different kinds of value types." + } +} diff --git a/apps/docs/docs/user/value-types/_category_.json.license b/apps/docs/docs/user/value-types/_category_.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/docs/user/value-types/_category_.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/docs/user/value-types/primitive-value-types.md b/apps/docs/docs/user/value-types/primitive-value-types.md new file mode 100644 index 00000000..4fac4b44 --- /dev/null +++ b/apps/docs/docs/user/value-types/primitive-value-types.md @@ -0,0 +1,47 @@ +--- +sidebar_position: 2 +--- + +# Primitive Value Types + +_Primitive value types_ are based on _built-in value types_ and use a collection of _constraints_ to restrict the range of valid values. +Such _constraints_ are implicitly connected via a logical `AND` relation. +Note that the _constraints_ need to be applicable to the base-type of the _value type_ - indicated by the identifier after the keyword `oftype`: + +```jayvee +valuetype GasFillLevel oftype integer { + constraints: [ GasFillLevelRange ]; +} +``` + +## Constraints + +_Constraints_ for _value types_ declare the validity criteria that each concrete value is checked against. + +### Syntax 1: Expression syntax + +The syntax of expression-based _constraints_ uses an expression that evaluates to `true` or `false` for the given `value`. The type of the values the expression is working in is indicated ofter the keyword `on`: + +```jayvee +constraint GasFillLevelRange on decimal: + value >= 0 and value <= 100; +``` + +Refer to the [expression documentation](../expressions.md) for further reading on expressions. + +### Syntax 2: Block-like syntax + +The syntax of _constraints_ is similar to the syntax of _blocks_. +The availability of property keys and their respective _value types_ is determined by the type of the _constraint_ - indicated by the identifier after the keyword `oftype`: + +```jayvee +constraint GasFillLevelRange oftype RangeConstraint { + lowerBound: 0; + lowerBoundInclusive: true; + upperBound: 100; + upperBoundInclusive: true; +} +``` + +Note that the type of _constraint_ also determines its applicability to _value types_. +For instance, a `RangeConstraint` can only be applied to the numerical types `integer` and `decimal`. diff --git a/apps/docs/docs/user/value-types/primitive-value-types.md.license b/apps/docs/docs/user/value-types/primitive-value-types.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/docs/user/value-types/primitive-value-types.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/docusaurus.config.js b/apps/docs/docusaurus.config.js new file mode 100644 index 00000000..e88e9ce9 --- /dev/null +++ b/apps/docs/docusaurus.config.js @@ -0,0 +1,136 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// @ts-check +// Note: type annotations allow type checking and IDEs autocompletion + +const lightCodeTheme = require('prism-react-renderer/themes/github'); +const darkCodeTheme = require('prism-react-renderer/themes/dracula'); + +/** @type {import('@docusaurus/types').Config} */ +const config = { + title: 'Jayvee', + url: 'https://jvalue.github.io', + baseUrl: '/jayvee', + onBrokenLinks: 'throw', + onBrokenMarkdownLinks: 'warn', + favicon: '/img/jayvee.png', + organizationName: 'jvalue', // Usually your GitHub org/user name. + projectName: 'jayvee', // Usually your repo name. + themes: ['@docusaurus/theme-mermaid'], + markdown: { + mermaid: true, + }, + + presets: [ + [ + 'classic', + /** @type {import('@docusaurus/preset-classic').Options} */ + ({ + docs: { + sidebarPath: require.resolve('./sidebars.js'), + }, + theme: { + customCss: require.resolve('./src/css/custom.css'), + }, + }), + ], + ], + + themeConfig: + /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ + ({ + navbar: { + title: 'Jayvee', + items: [ + { + type: 'doc', + docId: 'user/intro/intro', + position: 'left', + label: 'User Docs', + }, + { + type: 'doc', + docId: 'dev/intro', + position: 'left', + label: 'Developer Docs', + }, + { + type: 'docsVersionDropdown', + position: 'right', + dropdownActiveClassDisabled: true, + }, + { + href: 'https://github.com/jvalue/jayvee', + position: 'right', + className: 'header-github-link', + 'aria-label': 'GitHub repository', + }, + ], + }, + footer: { + style: 'dark', + links: [ + { + title: 'Learn', + items: [ + { + label: 'Installation', + to: '/docs/user/intro', + }, + { + label: 'Core Concepts', + to: '/docs/user/core-concepts', + }, + { + label: 'Examples', + to: '/docs/user/examples/cars', + }, + { + label: 'Start contributing', + to: '/docs/dev/intro', + }, + ], + }, + { + title: 'More', + items: [ + { + label: 'GitHub', + href: 'https://github.com/jvalue/jayvee', + }, + { + label: 'JValue', + href: 'https://jvalue.org/', + }, + ], + }, + { + title: 'Legal', + items: [ + { + label: 'Imprint', + href: 'https://jvalue.org/notices/imprint/', + }, + { + label: 'Privacy Policy', + href: 'https://jvalue.org/notices/privacy-policy/', + }, + { + label: 'Open Source Notices', + href: 'https://github.com/jvalue/jayvee/blob/main/NOTICES.md', + }, + ], + }, + ], + copyright: `Copyright © ${new Date().getFullYear()} Friedrich-Alexander Universität Erlangen-Nürnberg.`, + }, + prism: { + theme: lightCodeTheme, + darkTheme: darkCodeTheme, + }, + }), +}; + +module.exports = config; diff --git a/apps/docs/project.json b/apps/docs/project.json new file mode 100644 index 00000000..39b7bfd0 --- /dev/null +++ b/apps/docs/project.json @@ -0,0 +1,42 @@ +{ + "name": "docs", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "sourceRoot": "apps/docs/src", + "targets": { + "build": { + "executor": "@nx-plus/docusaurus:browser", + "options": { + "outputPath": "dist/apps/docs" + }, + "dependsOn": ["generate"] + }, + "serve": { + "executor": "@nx-plus/docusaurus:dev-server", + "options": { + "port": 3000 + }, + "dependsOn": ["generate"] + }, + "generate": { + "executor": "nx:run-commands", + "options": { + "command": "nx start docs-generator", + "parallel": false + } + }, + "lint": { + "executor": "@nx/eslint:lint", + "outputs": ["{options.outputFile}"] + }, + "version-snapshot": { + "executor": "nx:run-commands", + "dependsOn": ["generate"], + "options": { + "commands": ["node tools/scripts/docs/create-new-version-snapshot.mjs"], + "parallel": false + } + } + }, + "tags": [] +} diff --git a/apps/docs/project.json.license b/apps/docs/project.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/project.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/sidebars.js b/apps/docs/sidebars.js new file mode 100644 index 00000000..b3ed6297 --- /dev/null +++ b/apps/docs/sidebars.js @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** + * Creating a sidebar enables you to: + - create an ordered group of docs + - render a sidebar for each doc of that group + - provide next/previous navigation + + The sidebars can be generated from the filesystem, or explicitly defined here. + + Create as many sidebars as you want. + */ + +// @ts-check + +/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ +const sidebars = { + // By default, Docusaurus generates a sidebar from the docs folder structure + userDocsSidebar: [{ type: 'autogenerated', dirName: 'user' }], + devDocsSidebar: [{ type: 'autogenerated', dirName: 'dev' }], + + // But you can create a sidebar manually + /* + tutorialSidebar: [ + { + type: 'category', + label: 'Tutorial', + items: ['hello'], + }, + ], + */ +}; + +module.exports = sidebars; diff --git a/apps/docs/src/components/HomepageFeatures/index.tsx b/apps/docs/src/components/HomepageFeatures/index.tsx new file mode 100644 index 00000000..4cdf7ac6 --- /dev/null +++ b/apps/docs/src/components/HomepageFeatures/index.tsx @@ -0,0 +1,48 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import React from 'react'; +import clsx from 'clsx'; +import styles from './styles.module.css'; +import { useBaseUrlUtils } from '@docusaurus/useBaseUrl'; + +import { type FeatureItem, Features } from '../../data/features'; + +function Feature(feature: FeatureItem) { + const {withBaseUrl} = useBaseUrlUtils(); + + return ( + <div className={clsx('col col--4')}> + <div className="text--center"> + <img + className={styles.featureImage} + alt={feature.title} + src={withBaseUrl(feature.image.src)} + width={Math.floor(feature.image.width)} + height={Math.floor(feature.image.height)} + loading="lazy" + /> + </div> + <div className="text--center padding-horiz--md"> + <h3>{feature.title}</h3> + <div>{feature.description}</div> + </div> + </div> + ); +} + +export default function HomepageFeatures(): JSX.Element { + return ( + <section className={styles.features}> + <div className="container"> + <h2 className='text--center'>Main Features</h2> + <div className="row"> + {Features.map((props, idx) => ( + <Feature key={idx} {...props} /> + ))} + </div> + </div> + </section> + ); +} diff --git a/apps/docs/src/components/HomepageFeatures/styles.module.css b/apps/docs/src/components/HomepageFeatures/styles.module.css new file mode 100644 index 00000000..62da5396 --- /dev/null +++ b/apps/docs/src/components/HomepageFeatures/styles.module.css @@ -0,0 +1,12 @@ +/* + * SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +.features { + display: flex; + align-items: center; + padding: 2rem 0; + width: 100%; +} \ No newline at end of file diff --git a/apps/docs/src/css/custom.css b/apps/docs/src/css/custom.css new file mode 100644 index 00000000..dead4e6e --- /dev/null +++ b/apps/docs/src/css/custom.css @@ -0,0 +1,53 @@ +/* + * SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +/** + * Any CSS included here will be global. The classic template + * bundles Infima by default. Infima is a CSS framework designed to + * work well for content-centric websites. + */ + +/* You can override the default Infima variables here. */ +:root { + --ifm-color-primary: #2e8555; + --ifm-color-primary-dark: #29784c; + --ifm-color-primary-darker: #277148; + --ifm-color-primary-darkest: #205d3b; + --ifm-color-primary-light: #33925d; + --ifm-color-primary-lighter: #359962; + --ifm-color-primary-lightest: #3cad6e; + --ifm-code-font-size: 95%; + --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); +} + +/* For readability concerns, you should choose a lighter palette in dark mode. */ +[data-theme='dark'] { + --ifm-color-primary: #25c2a0; + --ifm-color-primary-dark: #21af90; + --ifm-color-primary-darker: #1fa588; + --ifm-color-primary-darkest: #1a8870; + --ifm-color-primary-light: #29d5b0; + --ifm-color-primary-lighter: #32d8b4; + --ifm-color-primary-lightest: #4fddbf; + --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); +} + +.header-github-link:before { + background: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E") no-repeat; + display: flex; + height: 24px; + width: 24px; + content: ""; +} +.header-github-link:hover { + opacity: 0.6; +} + +.navbar__title { + background: linear-gradient(45deg, var(--ifm-color-primary), var(--ifm-color-primary-lightest) 80%); + background-clip: text; + -webkit-text-fill-color: transparent; +} \ No newline at end of file diff --git a/apps/docs/src/data/features.tsx b/apps/docs/src/data/features.tsx new file mode 100644 index 00000000..b8e20f7c --- /dev/null +++ b/apps/docs/src/data/features.tsx @@ -0,0 +1,105 @@ +/* + * SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import React from 'react'; + +export interface FeatureItem { + title: string; + image: { + src: string; + height: number; + width: number; + }; + description: JSX.Element; +} + +export const Features: FeatureItem[] = [ + { + title: 'Focused', + description: ( + <p> + Jayvee is a <b>DSL</b> (domain-specific language){' '} + <b>tailored for data engineering</b>. + </p> + ), + image: { + src: '/img/features/focus.svg', + width: 100, + height: 100, + }, + }, + { + title: 'Text-based', + description: ( + <p> + Jayvee's text-based syntax allows <b>reuse</b> of existing{' '} + <b>collaboration infrastructure</b> from open source software + development. + </p> + ), + image: { + src: '/img/features/text.svg', + width: 100, + height: 100, + }, + }, + { + title: 'Open', + description: ( + <p> + Jayvee is open for extension (<b>no vendor lock-in</b>) with a{' '} + <b>fast innovation in tools</b>. + </p> + ), + image: { + src: '/img/features/open.svg', + width: 100, + height: 100, + }, + }, + { + title: 'Accessible', + description: ( + <p> + Jayvee enables <b>collaboration with subject matter experts</b> that + might not be professional programmers. + </p> + ), + image: { + src: '/img/features/accessible.svg', + width: 100, + height: 100, + }, + }, + { + title: 'Maintainable', + description: ( + <p> + Jayvee passes on hidden magic to make the{' '} + <b>code more self-explaining</b>. + </p> + ), + image: { + src: '/img/features/maintainable.svg', + width: 100, + height: 100, + }, + }, + { + title: 'Empirical Design', + description: ( + <p> + The design of Jayvee is <b>validated with scientific methods</b> like + surveys and controlled experiments instead of relying on gut feeling. + </p> + ), + image: { + src: '/img/features/research.svg', + width: 100, + height: 100, + }, + }, +]; diff --git a/apps/docs/src/pages/index.module.css b/apps/docs/src/pages/index.module.css new file mode 100644 index 00000000..de3425b0 --- /dev/null +++ b/apps/docs/src/pages/index.module.css @@ -0,0 +1,74 @@ +/* + * SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +/** + * CSS files with the .module.css suffix will be treated as CSS modules + * and scoped locally. + */ + +.titleBanner { + text-align: center; + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + position: relative; + overflow: hidden; + color: aliceblue; + background-color: #2b3137; + padding: 4rem; +} + +@media screen and (max-width: 996px) { + .titleBanner { + padding: 2rem; + } +} + +.titleBannerTitle { + font-size: 64px; + font-weight: bold; + margin: 0.5rem 0; +} + +.titleBannerTitleText { + background: linear-gradient(45deg, var(--ifm-color-primary), var(--ifm-color-info) 80%); + background-clip: text; + -webkit-text-fill-color: transparent; +} + +.titleBannerTagline { + font-size: 32px; +} + +.titleBannerTagline b { + color: var(--ifm-color-primary); +} + +.titleBannerButtons { + --ifm-button-size-multiplier: 1.5; + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: center; + margin-top: 12px; +} + +.titleBannerButtons a, +.titleBannerButtons a:hover { + color: black; + margin: 12px 12px 0px 12px; +} + +.titleBannerButtonsGitHubButtonWrapper { + margin-top: 12px; + margin-left: 12px; + display: flex; +} + +.titleBannerButtonsGitHubButton { + overflow: hidden; +} \ No newline at end of file diff --git a/apps/docs/src/pages/index.tsx b/apps/docs/src/pages/index.tsx new file mode 100644 index 00000000..023b40d7 --- /dev/null +++ b/apps/docs/src/pages/index.tsx @@ -0,0 +1,79 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import React from 'react'; +import clsx from 'clsx'; +import Link from '@docusaurus/Link'; +import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; +import Layout from '@theme/Layout'; +import HomepageFeatures from '@site/src/components/HomepageFeatures'; + +import MascotImageUrl from '@site/static/img/mascots/mascot1.png'; + +import styles from './index.module.css'; + +function HomepageHeader() { + return ( + <div> + <TitleBanner /> + </div> + ); +} + +function TitleBanner(): JSX.Element { + return ( + <header className={clsx('hero hero--primary', styles.titleBanner)}> + <div className="container"> + <div className={styles.titleBannerTitle}> + 🥳 + <span className={styles.titleBannerTitleText}> + <b>Jayvee's</b> Alpha is now available! + </span> + 🎉 + </div> + + <div className={styles.titleBannerTagline}> + A <b>domain-specific language</b> for everyone to <b>participate</b>{' '} + in building <b>data pipelines</b>! + </div> + + <div className={styles.titleBannerButtons}> + <span className="mascot-container"> + <img src={MascotImageUrl} width="120px" /> + </span> + <Link className="button button--primary" to="/docs/user/intro"> + Get Started + </Link> + <Link className="button button--info" to="/docs/user/examples/cars"> + Example + </Link> + <span className={styles.titleBannerButtonsGitHubButtonWrapper}> + <iframe + className={styles.titleBannerButtonsGitHubButton} + src="https://ghbtns.com/github-btn.html?user=jvalue&repo=jayvee&type=star&count=true&size=large" + width={160} + height={30} + title="GitHub Stars" + /> + </span> + </div> + </div> + </header> + ); +} + +export default function Home(): JSX.Element { + const { siteConfig } = useDocusaurusContext(); + return ( + <Layout + title={`${siteConfig.title}`} + description="Jayvee - Data Engineering made easy!" + > + <HomepageHeader /> + <main> + <HomepageFeatures /> + </main> + </Layout> + ); +} diff --git a/apps/docs/src/theme/prism-include-languages.js b/apps/docs/src/theme/prism-include-languages.js new file mode 100644 index 00000000..b7c451f1 --- /dev/null +++ b/apps/docs/src/theme/prism-include-languages.js @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** + * This file is mostly autogenerated using the following command: `docusaurus swizzle @docusaurus/theme-classic prism-include-languages` + * See this guide for details: https://docusaurus.io/docs/markdown-features/code-blocks#supported-languages + */ + +import siteConfig from '@generated/docusaurus.config'; +export default function prismIncludeLanguages(PrismObject) { + const { + themeConfig: { prism }, + } = siteConfig; + const { additionalLanguages } = prism; + // Prism components work on the Prism instance on the window, while prism- + // react-renderer uses its own Prism instance. We temporarily mount the + // instance onto window, import components to enhance it, then remove it to + // avoid polluting global namespace. + // You can mutate PrismObject: registering plugins, deleting languages... As + // long as you don't re-assign it + // eslint-disable-next-line no-undef + globalThis.Prism = PrismObject; + additionalLanguages.forEach((lang) => { + require(`prismjs/components/prism-${lang}`); + }); + // Add Jayvee language: + require('./prism-jayvee.js'); + // eslint-disable-next-line no-undef + delete globalThis.Prism; +} diff --git a/apps/docs/src/typings.d.ts b/apps/docs/src/typings.d.ts new file mode 100644 index 00000000..854c7a61 --- /dev/null +++ b/apps/docs/src/typings.d.ts @@ -0,0 +1,7 @@ +/* + * SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +declare module '*.module.css'; diff --git a/apps/docs/static/.nojekyll b/apps/docs/static/.nojekyll new file mode 100644 index 00000000..e69de29b diff --git a/apps/docs/static/img/features/accessible.svg b/apps/docs/static/img/features/accessible.svg new file mode 100644 index 00000000..4b0dc87a --- /dev/null +++ b/apps/docs/static/img/features/accessible.svg @@ -0,0 +1 @@ +<svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="m15 4v7h-9.83l-1.17 1.17v-8.17zm1-2h-13a1 1 0 0 0 -1 1v14l4-4h10a1 1 0 0 0 1-1v-9a1 1 0 0 0 -1-1m5 4h-2v9h-13v2a1 1 0 0 0 1 1h11l4 4v-15a1 1 0 0 0 -1-1z"/></svg> \ No newline at end of file diff --git a/apps/docs/static/img/features/accessible.svg.license b/apps/docs/static/img/features/accessible.svg.license new file mode 100644 index 00000000..5a67ce0b --- /dev/null +++ b/apps/docs/static/img/features/accessible.svg.license @@ -0,0 +1,5 @@ +SPDX-FileCopyrightText: Iconduck + +SPDX-License-Identifier: Apache-2.0 + +Source: https://iconduck.com/icons/89249/forum-outline \ No newline at end of file diff --git a/apps/docs/static/img/features/focus.svg b/apps/docs/static/img/features/focus.svg new file mode 100644 index 00000000..13ce4f73 --- /dev/null +++ b/apps/docs/static/img/features/focus.svg @@ -0,0 +1 @@ +<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="m21 11h-1.07a8 8 0 0 0 -6.93-6.93v-1.07a1 1 0 0 0 -2 0v1.07a8 8 0 0 0 -6.93 6.93h-1.07a1 1 0 0 0 0 2h1.07a8 8 0 0 0 6.93 6.93v1.07a1 1 0 0 0 2 0v-1.07a8 8 0 0 0 6.93-6.93h1.07a1 1 0 0 0 0-2zm-9 7a6 6 0 1 1 6-6 6 6 0 0 1 -6 6zm0-9a3 3 0 1 0 3 3 3 3 0 0 0 -3-3zm0 4a1 1 0 1 1 1-1 1 1 0 0 1 -1 1z"/></svg> \ No newline at end of file diff --git a/apps/docs/static/img/features/focus.svg.license b/apps/docs/static/img/features/focus.svg.license new file mode 100644 index 00000000..6b16e4e7 --- /dev/null +++ b/apps/docs/static/img/features/focus.svg.license @@ -0,0 +1,5 @@ +SPDX-FileCopyrightText: Iconduck + +SPDX-License-Identifier: Apache-2.0 + +Source: https://iconduck.com/icons/56427/crosshair \ No newline at end of file diff --git a/apps/docs/static/img/features/maintainable.svg b/apps/docs/static/img/features/maintainable.svg new file mode 100644 index 00000000..4c7dcacf --- /dev/null +++ b/apps/docs/static/img/features/maintainable.svg @@ -0,0 +1 @@ +<svg fill="none" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><g stroke="#000" stroke-width="2"><path d="m14 3.2686c0-.70063-.568-1.2686-1.2686-1.2686h-1.4628c-.7006 0-1.2686.56797-1.2686 1.2686 0 .57885-.39635 1.07442-.9348 1.28686-.08522.03362-.16973.06867-.25348.1051-.53102.231-1.16197.16088-1.57145-.24859-.49553-.49554-1.29895-.49554-1.79448 0l-1.03392 1.03391c-.49555.49555-.49555 1.29899 0 1.79454.40948.40948.4796 1.04044.24862 1.57147-.03641.0837-.07143.16815-.10503.25331-.21244.53845-.70802.9348-1.28686.9348-.70063 0-1.2686.568-1.2686 1.2686v1.4628c0 .7006.56797 1.2686 1.2686 1.2686.57884 0 1.07442.3963 1.28686.9348.0336.0852.06862.1696.10503.2533.23099.531.16086 1.162-.24862 1.5715-.49555.4955-.49555 1.299 0 1.7945l1.03391 1.0339c.49554.4956 1.29896.4956 1.7945 0 .40947-.4094 1.04043-.4796 1.57145-.2486.08375.0365.16825.0715.25347.1051.53845.2125.9348.7081.9348 1.2869 0 .7006.568 1.2686 1.2686 1.2686h1.4628c.7006 0 1.2686-.568 1.2686-1.2686 0-.5788.3963-1.0744.9348-1.2869.0852-.0336.1697-.0686.2534-.105.531-.231 1.1619-.1609 1.5713.2486.4955.4955 1.2989.4955 1.7944 0l1.0341-1.0341c.4955-.4955.4955-1.2988 0-1.7943-.4095-.4095-.4796-1.0404-.2486-1.5714.0365-.0837.0715-.1683.1051-.2535.2125-.5384.7081-.9348 1.2869-.9348.7006 0 1.2686-.568 1.2686-1.2686v-1.4628c0-.7006-.568-1.2686-1.2686-1.2686-.5788 0-1.0744-.39635-1.2869-.9348-.0336-.08524-.0686-.16976-.1051-.25354-.231-.53098-.1609-1.16189.2486-1.57133.4955-.4955.4955-1.29886 0-1.79436l-1.0341-1.03404c-.4955-.49551-1.2989-.49551-1.7944 0-.4094.40945-1.0403.47957-1.5713.24859-.0837-.03642-.1682-.07145-.2534-.10506-.5385-.21244-.9348-.70802-.9348-1.28686z"/><path d="m16 12c0 2.2091-1.7909 4-4 4-2.20914 0-4-1.7909-4-4 0-2.20914 1.79086-4 4-4 2.2091 0 4 1.79086 4 4z"/></g></svg> \ No newline at end of file diff --git a/apps/docs/static/img/features/maintainable.svg.license b/apps/docs/static/img/features/maintainable.svg.license new file mode 100644 index 00000000..331d9e3d --- /dev/null +++ b/apps/docs/static/img/features/maintainable.svg.license @@ -0,0 +1,5 @@ +SPDX-FileCopyrightText: Iconduck + +SPDX-License-Identifier: MIT + +Source: https://iconduck.com/icons/113189/gear \ No newline at end of file diff --git a/apps/docs/static/img/features/open.svg b/apps/docs/static/img/features/open.svg new file mode 100644 index 00000000..5477ec18 --- /dev/null +++ b/apps/docs/static/img/features/open.svg @@ -0,0 +1 @@ +<svg fill="none" height="20" viewBox="0 0 20 20" width="20" xmlns="http://www.w3.org/2000/svg"><path d="m4.02682 2.5v.5h-1.52682c-.27614 0-.5.22386-.5.5v13c0 .2761.22386.5.5.5h15c.2761 0 .5-.2239.5-.5v-13c0-.27614-.2239-.5-.5-.5h-1.5268v-.5c0-.27614-.2239-.5-.5-.5h-3.0281c-.9425 0-1.8255.40492-2.4451 1.0902-.61965-.68528-1.50259-1.0902-2.44505-1.0902h-3.02813c-.27614 0-.5.22386-.5.5zm10.94638.96416c-.0009.01184-.0013.02379-.0013.03584s.0004.024.0013.03584v10.40536h-2.353c-.7846 0-1.5271.271-2.1202.7433v-10.60561c.4263-.67222 1.1588-1.07889 1.9451-1.07889h2.5281zm1 .53584h1.0268v12h-6.373c.4515-.6592 1.1943-1.0588 1.9932-1.0588h2.853c.2761 0 .5-.2239.5-.5zm-8.59343 10.9412c.79892 0 1.54168.3996 1.99319 1.0588h-6.37296v-12h1.02682v10.4412c0 .2761.22386.5.5.5zm-2.35295-1v-10.40537c.00084-.01183.00127-.02378.00127-.03583s-.00043-.024-.00127-.03583v-.46417h2.52813c.78624 0 1.51879.40667 1.94505 1.07889v10.60561c-.59312-.4723-1.33559-.7433-2.12023-.7433z" fill="#212121"/></svg> \ No newline at end of file diff --git a/apps/docs/static/img/features/open.svg.license b/apps/docs/static/img/features/open.svg.license new file mode 100644 index 00000000..8514fd55 --- /dev/null +++ b/apps/docs/static/img/features/open.svg.license @@ -0,0 +1,5 @@ +SPDX-FileCopyrightText: Iconduck + +SPDX-License-Identifier: MIT + +Source: https://iconduck.com/icons/66734/book-open \ No newline at end of file diff --git a/apps/docs/static/img/features/research.svg b/apps/docs/static/img/features/research.svg new file mode 100644 index 00000000..c1a9f3e2 --- /dev/null +++ b/apps/docs/static/img/features/research.svg @@ -0,0 +1 @@ +<svg height="32" viewBox="0 0 32 32" width="32" xmlns="http://www.w3.org/2000/svg"><path d="m25.3943 24a7.8772 7.8772 0 0 0 -1.6707-8.5684 3.918 3.918 0 0 0 -1.0844-4.414l2.7759-2.7759a2.0025 2.0025 0 0 0 0-2.8286l-2.8282-2.8282a2.0021 2.0021 0 0 0 -2.8286 0l-13.1724 13.1724a2.0027 2.0027 0 0 0 0 2.8286l2.8282 2.8282a2.0024 2.0024 0 0 0 2.8286 0l4.7749-4.7754a3.9329 3.9329 0 0 0 5.5139.4326 5.9442 5.9442 0 0 1 .646 6.9287h-7.1775v4h-12v2h24v-6zm-14.5662-4-2.8281-2.8286 1.8787-1.8784 2.8283 2.8281zm5.1719-6a3.9811 3.9811 0 0 0 .0762.7524l-1.9551 1.9546-2.8284-2.8281 9.88-9.88 2.8283 2.8282-3.2488 3.2491a3.9771 3.9771 0 0 0 -4.7522 3.9238zm4 2a2 2 0 1 1 2-2 2.0023 2.0023 0 0 1 -2 2zm6 12h-8v-2h8z"/><path d="m0 0h32v32h-32z" fill="none"/></svg> \ No newline at end of file diff --git a/apps/docs/static/img/features/research.svg.license b/apps/docs/static/img/features/research.svg.license new file mode 100644 index 00000000..879e6c6d --- /dev/null +++ b/apps/docs/static/img/features/research.svg.license @@ -0,0 +1,5 @@ +SPDX-FileCopyrightText: Iconduck + +SPDX-License-Identifier: Apache-2.0 + +Source: https://iconduck.com/icons/9483/microscope \ No newline at end of file diff --git a/apps/docs/static/img/features/text.svg b/apps/docs/static/img/features/text.svg new file mode 100644 index 00000000..fc25dca1 --- /dev/null +++ b/apps/docs/static/img/features/text.svg @@ -0,0 +1,6 @@ +<svg width="24" height="24" stroke-width="1.5" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M3 10L17 10" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/> +<path d="M3 6H21" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/> +<path d="M3 18L17 18" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/> +<path d="M3 14H21" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/> +</svg> diff --git a/apps/docs/static/img/features/text.svg.license b/apps/docs/static/img/features/text.svg.license new file mode 100644 index 00000000..26427708 --- /dev/null +++ b/apps/docs/static/img/features/text.svg.license @@ -0,0 +1,5 @@ +SPDX-FileCopyrightText: Iconduck + +SPDX-License-Identifier: MIT + +Source: https://iconduck.com/icons/124267/align-left \ No newline at end of file diff --git a/apps/docs/static/img/mascots/mascot1.png b/apps/docs/static/img/mascots/mascot1.png new file mode 100644 index 00000000..6d1d780d Binary files /dev/null and b/apps/docs/static/img/mascots/mascot1.png differ diff --git a/apps/docs/static/img/mascots/mascot1.png.license b/apps/docs/static/img/mascots/mascot1.png.license new file mode 100644 index 00000000..ee78e353 --- /dev/null +++ b/apps/docs/static/img/mascots/mascot1.png.license @@ -0,0 +1,7 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only + +Info: This image is based on a generated image by Midjourney (Basic subscription). +Midjourney ToS: https://docs.midjourney.com/docs/terms-of-service or archived on https://web.archive.org/web/20240205060308/https://docs.midjourney.com/docs/terms-of-service. +Date of creation: 2024-02-06 diff --git a/apps/docs/static/img/mascots/mascot2.png b/apps/docs/static/img/mascots/mascot2.png new file mode 100644 index 00000000..b7b9adf1 Binary files /dev/null and b/apps/docs/static/img/mascots/mascot2.png differ diff --git a/apps/docs/static/img/mascots/mascot2.png.license b/apps/docs/static/img/mascots/mascot2.png.license new file mode 100644 index 00000000..ee78e353 --- /dev/null +++ b/apps/docs/static/img/mascots/mascot2.png.license @@ -0,0 +1,7 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only + +Info: This image is based on a generated image by Midjourney (Basic subscription). +Midjourney ToS: https://docs.midjourney.com/docs/terms-of-service or archived on https://web.archive.org/web/20240205060308/https://docs.midjourney.com/docs/terms-of-service. +Date of creation: 2024-02-06 diff --git a/apps/docs/static/img/mascots/mascot3.png b/apps/docs/static/img/mascots/mascot3.png new file mode 100644 index 00000000..9c0d1040 Binary files /dev/null and b/apps/docs/static/img/mascots/mascot3.png differ diff --git a/apps/docs/static/img/mascots/mascot3.png.license b/apps/docs/static/img/mascots/mascot3.png.license new file mode 100644 index 00000000..ee78e353 --- /dev/null +++ b/apps/docs/static/img/mascots/mascot3.png.license @@ -0,0 +1,7 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only + +Info: This image is based on a generated image by Midjourney (Basic subscription). +Midjourney ToS: https://docs.midjourney.com/docs/terms-of-service or archived on https://web.archive.org/web/20240205060308/https://docs.midjourney.com/docs/terms-of-service. +Date of creation: 2024-02-06 diff --git a/apps/docs/tsconfig.app.json b/apps/docs/tsconfig.app.json new file mode 100644 index 00000000..84170911 --- /dev/null +++ b/apps/docs/tsconfig.app.json @@ -0,0 +1,21 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "jsx": "react", + "types": ["node"], + "lib": ["DOM", "ESNext"], + "esModuleInterop": true, + "skipLibCheck": false + }, + "exclude": ["src/**/*.spec.ts", "src/**/*.test.ts"], + "include": ["src/**/*.ts", "src/**/*.tsx"], + "types": [ + "node", + "@docusaurus/module-type-aliases", + "@docusaurus/theme-classic" + ], + "baseUrl": ".", + "paths": { + "@site/*": ["./*"] + } +} diff --git a/apps/docs/tsconfig.app.json.license b/apps/docs/tsconfig.app.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/tsconfig.app.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/tsconfig.json b/apps/docs/tsconfig.json new file mode 100644 index 00000000..63dbe35f --- /dev/null +++ b/apps/docs/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../tsconfig.base.json", + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/apps/docs/tsconfig.json.license b/apps/docs/tsconfig.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/tsconfig.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.16/dev/01-intro.md b/apps/docs/versioned_docs/version-0.0.16/dev/01-intro.md new file mode 100644 index 00000000..94bc0205 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/dev/01-intro.md @@ -0,0 +1,52 @@ +--- +sidebar_position: 1 +--- + +# Introduction for Jayvee Developers + +## How to contribute + +In order to contribute to the Jayvee project, please have a look at our [contribution guide](https://github.com/jvalue/jayvee/blob/main/CONTRIBUTING.md). + +The overall project is licensed under the `AGPL-3.0-only` license and is compliant to the [REUSE Specification](https://reuse.software/spec/) by the [Free Software Foundation Europe](https://fsfe.org/). +Because of this, contributions are required to adhere to the license and follow that specification. +More details on this topic can be found [here](./02-licensing-and-copyright.md). + +And last but not least, please read and follow our [code of conduct](https://github.com/jvalue/jayvee/blob/main/CODE_OF_CONDUCT.md). + +## Project overview + +The Jayvee repository is located [here](https://github.com/jvalue/jayvee) on GitHub. +It uses an [Nx mono-repository](https://nx.dev/) setup and contains all related projects, most notably the Jayvee language server and interpreter. +Please have a look at the [architecture overview](./04-architecture-overview.md) for more details. + +## Prerequisites + +Node.js and npm are required for development. +It is recommended to use their LTS version to avoid any potential compatibility issues. +Also, refer to this [quick start guide](https://github.com/jvalue/jayvee#development-quickstart) for developers. + +In order to run the Jayvee VS Code extension during development, VS Code is required as IDE. +Using VS Code also allows installing the [Langium VS Code extension](https://marketplace.visualstudio.com/items?itemName=langium.langium-vscode) for better IDE support when working with Langium grammar files. + +## Resources for getting started + +### Langium + +- [**Langium documentation**](https://langium.org/docs/) +- Practical examples using Langium: + - [Langium examples](https://github.com/langium/langium/tree/main/examples): Official example languages by Langium + - [Langium's grammar language](https://github.com/langium/langium/tree/main/packages/langium): The implementation of Langium's own grammar language + - [Language server for SQL](https://github.com/langium/langium-sql): An implementation of a language server for SQL + - [MiniLogo DSL](https://github.com/langium/langium-minilogo): A domain-specific language for drawing pictures + - [Lox language](https://github.com/langium/langium-lox): An implementation of the Lox scripting language (also see the "Crafting Interpreters" book below) + - [SimpleUI DSL](https://github.com/TypeFox/langium-ui-framework): A domain-specific language for generating user interfaces + - [STPA-DSL](https://github.com/kieler/stpa): A domain-specific language for System-Theoretic Process Analysis +- [Langium playground](https://langium.org/playground/): A tool for testing Langium grammars and visualizing resulting ASTs +- [Typefox blog](https://www.typefox.io/blog/): A blog by the company behind Langium, mostly posting Langium-related content. + +### Building compilers / interpreters / DSLs + +- [Crafting Interpreters](https://craftinginterpreters.com/contents.html): A book by Robert Nystrom on implementing an interpreter for the Lox scripting language +- [DSL Engineering](https://voelter.de/dslbook/markusvoelter-dslengineering-1.0.pdf): A book by Markus Voelter on designing, implementing and using domain-specific languages +- [Awesome Compilers](https://github.com/aalhour/awesome-compilers#readme): A list featuring many resources on compilers and interpreters diff --git a/apps/docs/versioned_docs/version-0.0.16/dev/01-intro.md.license b/apps/docs/versioned_docs/version-0.0.16/dev/01-intro.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/dev/01-intro.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.16/dev/02-licensing-and-copyright.md b/apps/docs/versioned_docs/version-0.0.16/dev/02-licensing-and-copyright.md new file mode 100644 index 00000000..6d29c2e2 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/dev/02-licensing-and-copyright.md @@ -0,0 +1,68 @@ +--- +title: Licensing and copyright +--- + +The [Jayvee repository](https://github.com/jvalue/jayvee) is REUSE compliant, meaning that it fulfills the +[REUSE Specification](https://reuse.software/spec/) by the [Free Software Foundation Europe](https://fsfe.org/) (FSFE). +This makes clear under which license each project file is licensed under and who owns the copyright, not only for +humans but also for machines. + +This is done by explicitly adding copyright and licensing information to each file of the project. This is achieved +by either using a comment header or a separate `*.license` file in case comments are not possible. + +See <https://reuse.software/> more information. + +## What license is used in this project and who is the copyright holder? + +The entire project is licensed under [AGPL-3.0-only](https://spdx.org/licenses/AGPL-3.0-only.html), the +license file can be found [here](https://github.com/jvalue/jayvee/blob/main/LICENSES/AGPL-3.0-only.txt). +The copyright holder is the [Friedrich-Alexander-Universität Erlangen-Nürnberg](https://www.fau.eu/). + +## How to submit a REUSE compliant contribution? + +In case you want to contribute to the project, you will need to ensure that all of your contributed files are REUSE +compliant. In order to achieve this, you need to add a key-value pair for both copyright and licensing information +following this schema: + +``` +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only +``` + +In case a file allows comments, use single-line comments to add the copyright and licensing information at the top +of the file. Otherwise, create a corresponding `*.license` file with the text above as its content. You can have a +look at some existing project files to get an impression on it is done in practice. + +For files with common file extensions, you can use the [reuse CLI tool](https://github.com/fsfe/reuse-tool) to add +licensing and copyright information automatically. + +For more details, you can have a look at the [Getting Started tutorial](https://reuse.software/tutorial/) on the REUSE +website. + +## How to validate REUSE compliance? + +When you make a contribution and open a new pull request, the CI checks whether your contribution is REUSE compliant +using the [reuse CLI tool](https://github.com/fsfe/reuse-tool). + +In order to validate REUSE compliance in your local development environment, you have to install the +[reuse CLI tool](https://github.com/fsfe/reuse-tool) and run the following command in the projects' root folder: + +```bash +reuse lint +``` + +You can also set up a pre-commit hook, so the above command is run before each commit. +See [here](https://reuse.readthedocs.io/en/latest/readme.html#run-as-pre-commit-hook) for details on how to set it up. + +## How to hide `*.license` files in IDEs + +During development, the file explorer of your IDE may be cluttered due to the numerous `*.license` files in the +project. Luckily, most IDEs allow hiding certain files, e.g. by specifying a pattern to exclude them from the +explorer. + +Below, you can find instructions on how to hide `*.license` files in commonly used IDEs: +- **Visual Studio Code**: Add `**/*.license` to the +[`files.exclude` setting](https://code.visualstudio.com/docs/getstarted/userinterface#_explorer) +- **WebStorm**: Add `*.license` to the +[excluded files setting](https://www.jetbrains.com/help/webstorm/configuring-project-structure.html#exclude-by-pattern) diff --git a/apps/docs/versioned_docs/version-0.0.16/dev/02-licensing-and-copyright.md.license b/apps/docs/versioned_docs/version-0.0.16/dev/02-licensing-and-copyright.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/dev/02-licensing-and-copyright.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.16/dev/03-rfc-process.md b/apps/docs/versioned_docs/version-0.0.16/dev/03-rfc-process.md new file mode 100644 index 00000000..82376c4c --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/dev/03-rfc-process.md @@ -0,0 +1,12 @@ +--- +title: Language Design Process (RFCs) +--- + +We use RFCs to discuss changes to the language before implementing them. You can have a look at all closed (accepted / rejected) RFCs [here](https://github.com/jvalue/jayvee/pulls?q=is%3Apr+is%3Aclosed+RFC+), and all RFCs under discussion [here](https://github.com/jvalue/jayvee/pulls?q=is%3Apr+is%3Aopen+RFC). + +If you want to contribute an RFC please follow these steps: +1. Make a copy of the **template** at `rfc/0000-rfc-template.md` and place it into the `rfc` folder. +2. Create a draft for the RFC on a new branch. Follow the `TODOs` in template to do so. +3. Open a pull request with prefix `RFC <number>` in the title. +4. Address the reviews. Consider opening a new PR if major things need to be addressed and the discussion log becomes too confusing. +5. Once accepted, create an issue with the necessary steps to implement the RFC. \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.16/dev/03-rfc-process.md.license b/apps/docs/versioned_docs/version-0.0.16/dev/03-rfc-process.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/dev/03-rfc-process.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.16/dev/04-architecture-overview.md b/apps/docs/versioned_docs/version-0.0.16/dev/04-architecture-overview.md new file mode 100644 index 00000000..0fea2b37 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/dev/04-architecture-overview.md @@ -0,0 +1,42 @@ +--- +title: Architecture overview +--- + +Jayvee has a clear separation between the language itself and its execution. +This guide gives an overview of the overall architecture. + +## Language Server + +On the pure language side, the central project is the [language server](https://github.com/jvalue/jayvee/tree/main/libs/language-server) which is heavily built upon the [Langium framework](https://langium.org/). +It contains the syntax definition (i.e. the grammar) and is capable of performing static semantic analysis on models, so invalid models can be rejected and errors are reported to the user. +It uses the [Language Server Protocol](https://microsoft.github.io/language-server-protocol/) (LSP) for communicating with IDEs in order to provide common features such as diagnostics, auto completion and much more. + +## Interpreter + +The Jayvee [interpreter](https://github.com/jvalue/jayvee/tree/main/apps/interpreter) on the other hand is capable of running Jayvee models. +Therefore, it uses the language server as a library to perform lexing, parsing and semantic analysis on given models. +In case no errors were found during these phases, it executes the model by interpreting it. +This means that models are not compiled to any other language, like a compiler does, but rather directly executed on the fly without any code generation involved. +The interpreter comes with a command-line interface (CLI) for users, so they are able to execute Jayvee models easily. + +The following diagram visualizes the architecture described so far: + +```mermaid +graph LR +model[Jayvee Model] --> lexical(Lexical Analysis) +subgraph Jayvee Interpreter + subgraph Jayvee Language Server + subgraph Langium Framework + lexical --> syntax(Syntax Analysis) + end + syntax --> semantic(Semantic Analysis) + end + semantic --> interpretation(Interpretation) +end +``` + +## Jayvee Extensions + +Lastly, there are Jayvee extensions for adding additional features to Jayvee. +See [here](./05-jayvee-extensions.md) for more details. +It is worth pointing out that extensions also follow the separation between language and execution described above. diff --git a/apps/docs/versioned_docs/version-0.0.16/dev/04-architecture-overview.md.license b/apps/docs/versioned_docs/version-0.0.16/dev/04-architecture-overview.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/dev/04-architecture-overview.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.16/dev/05-jayvee-extensions.md b/apps/docs/versioned_docs/version-0.0.16/dev/05-jayvee-extensions.md new file mode 100644 index 00000000..01fa33a6 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/dev/05-jayvee-extensions.md @@ -0,0 +1,276 @@ +--- +title: Jayvee Extensions +--- + +## Concepts + +### Jayvee extension + +A Jayvee extension is used to add new block types to the language without having to modify the actual grammar of the language. Such a Jayvee extension usually consists of two parts: a language extension and an execution extension which are described in the upcoming two sections. + +Jayvee extensions that shall be used by default are bundled into the so-called [standard extension](https://github.com/jvalue/jayvee/tree/main/libs/extensions/std). That way, the [language server](https://github.com/jvalue/jayvee/tree/main/libs/language-server) and the [interpreter](https://github.com/jvalue/jayvee/tree/main/apps/interpreter) are able to load them out of the box. + +### Language extension + +A language extension defines meta information of block types which are required by the +[language server](https://github.com/jvalue/jayvee/tree/main/libs/language-server). +Such meta information describes properties of +block types such as their names, input / output types and their properties. + +Note that language extensions don't define any behavior. Instead, this is done by the corresponding execution extension. + +### Execution extension + +An execution extension defines behavior for block types. They build on the meta information from the corresponding +language extension, e.g. input / output types of the block need to match the signature of the execution method and +properties are accessed by their specified name. + +Execution extensions are only required by the [interpreter](https://github.com/jvalue/jayvee/tree/main/apps/interpreter) and not necessarily by the [language server](https://github.com/jvalue/jayvee/tree/main/libs/language-server) as they solely define behavior. + +## Recipes + +### Add a new Jayvee extension + +#### 1. Generate language and execution libraries + +```bash +npx nx g @nrwl/node:library --name="extensions/<extension-name>/lang" +npx nx g @nrwl/node:library --name="extensions/<extension-name>/exec" +``` + +#### 2. Create extension classes + +In `libs/extensions/<extension-name>/lang/src/extension.ts`: + +```typescript +import { + BlockMetaInformation, + ConstructorClass, + JayveeLangExtension, +} from '@jvalue/jayvee-language-server'; + +export class MyLangExtension implements JayveeLangExtension { + getBlockMetaInf(): Array<ConstructorClass<BlockMetaInformation>> { + return []; + } +} + +``` + +In `libs/extensions/<extension-name>/exec/src/extension.ts`: + +```typescript +import { BlockExecutorClass, JayveeExecExtension } from '@jvalue/jayvee-execution'; + +export class MyExecExtension implements JayveeExecExtension { + getBlockExecutors(): BlockExecutorClass[] { + return []; + } +} +``` + +#### 3. Export extension classes + +In `libs/extensions/<extension-name>/lang/src/index.ts`: + +```typescript +export * from './extension'; +``` + +In `libs/extensions/<extension-name>/exec/src/index.ts`: + +```typescript +export * from './extension'; +``` + +#### 4. Register new extension classes in the standard extension + +In `libs/extensions/std/lang/src/extension.ts`: + +```typescript +// ... + +import { MyLangExtension } from '@jvalue/jayvee-extensions/<extension-name>/lang'; + +export class StdLangExtension implements JayveeLangExtension { + private readonly wrappedExtensions: JayveeLangExtension[] = [ + // ... + // Register your language extension here: + new MyLangExtension(), + // ... + ]; + + // ... +} +``` + +In `libs/extensions/std/exec/src/extension.ts`: + +```typescript +// ... + +import { MyExecExtension } from '@jvalue/jayvee-extensions/<extension-name>/exec'; + +export class StdExecExtension implements JayveeExecExtension { + private readonly wrappedExtensions: JayveeExecExtension[] = [ + // ... + // Register your execution extension here: + new MyExecExtension(), + // ... + ]; + + // ... +} +``` + +### Add a new block type in a Jayvee extension + +#### 1. Implement `BlockMetaInformation` + +The following example defines a block type called `MyExtractor`. This block type, for instance, takes no input and +outputs a sheet. Furthermore, it defines two properties: + +- `url` of type text +- `limit` of type integer and default value `10` + - Considered optional due to the specified default value + +In `libs/extensions/<extension-name>/lang/src/lib/my-extractor-meta-inf.ts`: + +```typescript +import { + BlockMetaInformation, + IOType, + PrimitiveValuetypes, +} from '@jvalue/jayvee-language-server'; + +export class MyExtractorMetaInformation extends BlockMetaInformation { + constructor() { + super( + // How the block type should be called: + 'MyExtractor', + + // Property definitions: + { + url: { + type: PrimitiveValuetypes.Text, + }, + limit: { + type: PrimitiveValuetypes.Integer, + defaultValue: 10, + }, + }, + + // Input type: + IOType.NONE, + + // Output type: + IOType.SHEET, + ); + } +} +``` + +> **Note** +> Use `IOType.NONE` whenever you expect no input or output: +> +> - Use it as input type if you want to define an extractor +> - Use it as output type if you want to define a loader + +#### 2. Register the new `BlockMetaInformation` in the language extension + +In `libs/extensions/<extension-name>/lang/src/extension.ts`: + +```typescript +// ... + +import { MyExtractorMetaInformation } from './lib/my-extractor-meta-inf'; + +export class MyLangExtension implements JayveeLangExtension { + getBlockMetaInf(): Array<ConstructorClass<BlockMetaInformation>> { + return [ + // ... + // Register your meta information here: + MyExtractorMetaInformation, + // ... + ]; + } +} +``` + +#### 3. Implement `BlockExecutor` + +The following example implements an executor for the previously defined block type `MyExtractor`. + +The `execute` method defines the behavior when a block is executed. Its signature matches the input and output types defined in `MyExtractorMetaInformation`. + +In `libs/extensions/<extension-name>/exec/src/lib/my-extractor-executor.ts`: + +```typescript +import * as R from '@jvalue/jayvee-execution'; +import { + BlockExecutor, + BlockExecutorClass, + ExecutionContext, + Sheet, + implementsStatic, +} from '@jvalue/jayvee-execution'; +import { IOType, PrimitiveValuetypes } from '@jvalue/jayvee-language-server'; + +@implementsStatic<BlockExecutorClass>() +export class MyExtractorExecutor + implements BlockExecutor<IOType.NONE, IOType.SHEET> +{ + // Needs to match the type in meta information: + public static readonly type = 'MyExtractor'; + + public readonly inputType = IOType.NONE; + public readonly outputType = IOType.SHEET; + + async execute( + input: None, + context: ExecutionContext, + ): Promise<R.Result<Sheet>> { + // Accessing property values by their name: + const url = context.getPropertyValue( + 'url', + PrimitiveValuetypes.Text, + ); + const limit = context.getPropertyValue( + 'limit', + PrimitiveValuetypes.Integer, + ); + + // ... + + if (error) { + return R.err(...); + } + + return R.ok(...); + } +} +``` + +> **Warning** +> The generic types of `BlockExecutor<I,O>` need to match the input and output types of the corresponding `BlockMetaInformation`. + +#### 4. Register the new `BlockExecutor` in the execution extension + +In `libs/extensions/<extension-name>/exec/src/extension.ts`: + +```typescript +// ... + +import { MyExtractorExecutor } from './lib/my-extractor-executor'; + +export class MyExecExtension implements JayveeExecExtension { + getBlockExecutors(): BlockExecutorClass[] { + return [ + // ... + // Register your block executor here: + MyExtractorExecutor, + // ... + ]; + } +} +``` diff --git a/apps/docs/versioned_docs/version-0.0.16/dev/05-jayvee-extensions.md.license b/apps/docs/versioned_docs/version-0.0.16/dev/05-jayvee-extensions.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/dev/05-jayvee-extensions.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.16/dev/06-jayvee-grammar.md b/apps/docs/versioned_docs/version-0.0.16/dev/06-jayvee-grammar.md new file mode 100644 index 00000000..312c4544 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/dev/06-jayvee-grammar.md @@ -0,0 +1,33 @@ +--- +title: The Jayvee grammar +--- + +The grammar of Jayvee describes the syntax and structure of the language. +It is located in the language server project. +The grammar itself is written in [Langium's own grammar language](https://langium.org/docs/grammar-language/) which is similar to [Xtext](https://www.eclipse.org/Xtext/) grammars. +Such grammar files are easily identifiable via their `.langium` file extension. + +The grammar is used to generate TypeScript interfaces that represent the Abstract Syntax Tree (AST) and different, semantically equivalent files to define syntax highlighting for different applications. +For instance, a [TextMate](https://macromates.com/manual/en/language_grammars) file is generated for the syntax highlighting in the Jayvee VS Code extension whereas a [Monarch](https://microsoft.github.io/monaco-editor/monarch.html) file is generated for the syntax highlighting in the Monaco editor. + +For further information on the grammar language of Langium, visit the corresponding [Langium documentation](https://langium.org/docs/grammar-language/). + +## Working with the grammar + +To run the code generation, either use `npm run generate` for solely the code generation or `npm run build` for an entire build. + +Whenever the grammar is changed and the code generation is run during development, it is advisory to **close and reopen the IDE**, so the changes are noticed and the file indexing is updated. + +## How to rename AST nodes + +Renaming AST nodes is not as straight forward as one might initially assume. +When renaming individual rules in the grammar, just the generated TypeScript code in `ast.ts` will reflect the renaming, but not the rest of the codebase. + +The following steps explain how to rename an AST node, so the change is reflected in the entire codebase. As an example, an AST node called `A` is supposed to be renamed to `B`: + +- Open the `ast.ts` file in the language server project +- Locate the `A` interface / type and the `isA` typeguard +- Use the rename feature by the IDE to perform the renaming from `A` to `B` and from `isA` to `isB` +- Open the grammar and locate the `A` rule +- Use the rename feature by the Langium VS Code extension to perform the renaming from `A` to `B` +- Run `npm run generate` diff --git a/apps/docs/versioned_docs/version-0.0.16/dev/06-jayvee-grammar.md.license b/apps/docs/versioned_docs/version-0.0.16/dev/06-jayvee-grammar.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/dev/06-jayvee-grammar.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.16/dev/07-working-with-the-ast.md b/apps/docs/versioned_docs/version-0.0.16/dev/07-working-with-the-ast.md new file mode 100644 index 00000000..c44863a5 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/dev/07-working-with-the-ast.md @@ -0,0 +1,147 @@ +--- +title: Working with the AST +--- + +The nodes of the Abstract Syntax Tree (AST) consist of types and interfaces generated from the language grammar. +See [here](./06-jayvee-grammar.md) for more information on that topic. + +The following sections provide practical guides and tips for working with nodes of the AST. + +## Dealing with potentially incomplete AST nodes + +According to the generated interfaces, properties of AST nodes are never `undefined`. +In practice however, this is not always the case. + +For example, consider the language server being confronted with an incomplete Jayvee model with syntax errors. +In such cases, properties of AST nodes are in fact `undefined`, despite their interface definition. +In the interpreter however, after lexical and syntactic analysis succeeded, it can be assumed that all AST nodes are complete (i.e. they have no undefined properties) and even that all references are resolved. + +In order to avoid accessing properties of AST nodes that are potentially undefined, it is recommended to access them via the [`?.` operator](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#optional-chaining) rather than the regular `.` operator. +Note that this might result in a conflict with the ESLint rule [`@typescript-eslint/no-unnecessary-condition`](https://typescript-eslint.io/rules/no-unnecessary-condition/), so it has to be disabled manually for such cases. + +For disabling the rule in an entire file, place this comment below the copyright and licensing header: + +```typescript +/* eslint-disable @typescript-eslint/no-unnecessary-condition */ +``` + +For just a single line, place this comment above that particular line: + +```typescript +// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition +``` + +<details> + +<summary>Full example</summary> + +Consider an exemplary AST node `A` with a property `x` of type `string`. To access that property safely: + +```typescript +import { A } from './ast' + +const astNode: A; + +// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition +const property: string | undefined = astNode?.x; +``` + +</details> + +## Usage of `assertUnreachable` + +Most times, it is beneficial to make case distinctions exhaustive, especially when working with AST nodes, properties with a [union literal type](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types) or enums. +Exhaustiveness in this context means, that the TypeScript compiler is supposed to yield an error if a case distinction does not cover all possibilities. + +Langium offers a function called `assertUnreachable` which is capable of enforcing exhaustiveness and producing compiler errors in case of violations. See the following examples to get an idea on how to use it in practice: + +<details> + +<summary>Example for an exhaustive switch statement on a union literal type</summary> + +```typescript +import { assertUnreachable } from 'langium'; + +const operator: '+' | '-'; + +switch(operator) { + case '+': { + // ... + break; + } + case '-': { + // ... + break; + } + default: { + // To ensure the switch being exhaustive on `operator`: + assertUnreachable(operator); + } +} +``` + +</details> + +<details> + +<summary>Example for an exhaustive if-elseif-else cascade using typeguards</summary> + +Consider the exemplary AST nodes `A`, `B` and `C` and that `A = B | C`: + +```typescript +import { assertUnreachable } from 'langium'; +import { A, B, isB, C, isC } from './ast' + +const astNode: A; + +if (isB(astNode)) { + // `astNode` has type `B` here +} else if (isC(astNode)) { + // `astNode` has type `C` here +} else { + // To ensure the if-elseif-else cascade being exhaustive on `astNode`: + assertUnreachable(astNode); +} +``` + +</details> + +## Usage of `assert` for expressing runtime expectations + +During development, it may occur that a certain condition is expected to **be always true** at runtime. +For example, an AST node being of a certain type or a property being defined. +The type system of TypeScript is not always able to infer such facts, so developers may have to express these expectations explicitly in their code. +Examples are [type assertions](https://www.typescriptlang.org/docs/handbook/advanced-types.html) or the [non-null assertion operator](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html#non-null-assertion-operator). +Their usage may be problematic in case the condition does not always hold, e.g. due to a bug in the program or a wrong expectation by the programmer. +In such cases, it is hard to locate the origin and debug the program because such operations are erased in the compiled JavaScript code. + +To avoid these issues, it is recommended to express such expectations as boolean expressions that are actually validated at runtime. +This can be easily achieved by using the `assert` function. +It evaluates a given boolean expression and throws an `AssertionError` in case it evaluates to `false`. +After calling `assert`, the type system of TypeScript assumes the condition to be `true` and afterwards narrows types accordingly. + +Here is an example of how to use it in practice: + +```typescript +// Import the `assert` function like this: +import { strict as assert } from 'assert'; + +import { A, B, isB } from './ast'; + +const astNode: A; +assert(isB(astNode)); +// Here `astNode` has type `B` + +const referenced = astNode?.reference?.ref; +assert(referenced !== undefined); +// Here `referred` is not `undefined` +``` + +## AST wrapper classes + +The generated interfaces for AST nodes in `ast.ts` are only meant to represent the AST structurally, they don't define any behavior. +Also, in case of syntactic sugar, there may be different kinds of AST nodes representing the same semantic language concept (e.g. single pipes with a verbose syntax or chained pipes). + +To cope with this problem, there is the concept of an `AstNodeWrapper`. +An AST node wrapper is capable of wrapping AST nodes that represent the same semantic language concept and adding behavior to them via custom methods. +To get an impression on how this can be done in practice, please have a look at the [`PipeWrapper` class](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/lib/ast/wrappers/pipe-wrapper.ts) and the [`AstNodeWrapper` interface](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/lib/ast/wrappers/ast-node-wrapper.ts). diff --git a/apps/docs/versioned_docs/version-0.0.16/dev/07-working-with-the-ast.md.license b/apps/docs/versioned_docs/version-0.0.16/dev/07-working-with-the-ast.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/dev/07-working-with-the-ast.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.16/dev/08-validation-and-diagnostics.md b/apps/docs/versioned_docs/version-0.0.16/dev/08-validation-and-diagnostics.md new file mode 100644 index 00000000..98f03621 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/dev/08-validation-and-diagnostics.md @@ -0,0 +1,63 @@ +--- +title: Validation and diagnostics +--- + +Validation in the context of Jayvee can be understood as semantic analysis of Jayvee models. +The purpose is to uncover errors in models besides lexing, parsing and linking errors that the Langium framework already detects out of the box. +In case such errors are found, the language server makes the IDE display a diagnostic. +This means that the location of the error is underlined and an error message is shown. + +For example, each pipeline is expected to contain at least one starting block that requires no input. +Therefore, the language server analyzes every pipeline in a Jayvee model and checks for the presence of such a starting block. +In case no such block is found, the IDE underlines the pipeline definition in a red color and displays an error message. + +## How to implement validation + +### For arbitrary AST nodes + +In the file [`validation-registry.ts`](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/lib/validation/validation-registry.ts) of the language server, there is a registry containing validation functions for various AST nodes. +A validation function provided for a certain kind of AST node is called for every occurrence of such a node in a Jayvee model. +E.g. when registering a validation function for `PipelineDefinition` nodes, that function gets called once for each pipeline. + +A validation function always operates on a single, concrete AST node. +Besides the AST node, a `ValidationContext` object is passed to the function for reporting diagnostics via its `accept` method. + +The overall validation for a specific kind of AST node is usually subdivided into smaller checks that are called sequentially. +This potentially allows aborting the validation early, i.e. before all checks were run. +Aborting early may be useful if errors were reported during previous checks. +This can be determined via the `ValidationContext` which keeps track whether any errors occurred so far. + +A practical example where this plays a role are blocks: +When the type of a block is unknown to the language server, it makes no sense to validate inputs / outputs and properties of that block. +So, in case an error was reported during the check of the block type, the validation of that block is discontinued at that point. + +### For properties of blocks and constraints + +Validating property values of blocks / constraints works a bit differently because their individual validation is already incorporated into the overall validation framework. + +In general, the validation logic for a property values is located in the meta information of the block type / constraint type. +There, properties are defined using the `PropertySpecification` interface. +In order to add validation logic to a certain property, a validation function needs to be added to that property. + +For more complex validations that need to take multiple property values into account, it is also possible to provide a more general validation function which operates on the entire `PropertyBody` AST node. + +See [`text-range-selector-meta-inf.ts`](https://github.com/jvalue/jayvee/blob/main/libs/extensions/std/lang/src/text-range-selector-meta-inf.ts) for an example which includes both cases described above. + +## Reporting diagnostics + +During validation, diagnostics can be reported by calling the `accept` method of the given `ValidationContext` object. + +The call requires the severity of the diagnostic (`error`, `warning`, `info` or `hint`), the error message and its location. +The location is described by a `DiagnosticInfo` object which contains the affected AST node and optionally some further restrictions. +For more details, have a look at the [`DiagnosticInfo` interface](https://github.com/langium/langium/blob/main/packages/langium/src/validation/validation-registry.ts) by Langium. + +## Common issues + +When working on validations, a diagnostic with the following message may unexpectedly occur: `An error occurred during validation: ...` + +Such a diagnostic is generated by the Langium framework if an error object is thrown within the validation. +In most cases, the reason is the access of an `undefined` AST node property or an `AssertionError`. +For more details on such errors and how to avoid them, have a look at the documentation on [how to work with the AST](./07-working-with-the-ast.md). + +One way locate the origin of such errors is to debug the Jayvee VS Code extension, see [here](./10-debug-vs-code-extension.md) for more details. +Another possibility is to debug the interpreter and set appropriate breakpoints in the language server codebase. diff --git a/apps/docs/versioned_docs/version-0.0.16/dev/08-validation-and-diagnostics.md.license b/apps/docs/versioned_docs/version-0.0.16/dev/08-validation-and-diagnostics.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/dev/08-validation-and-diagnostics.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.16/dev/09-expressions-and-operators.md b/apps/docs/versioned_docs/version-0.0.16/dev/09-expressions-and-operators.md new file mode 100644 index 00000000..58c8c4c5 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/dev/09-expressions-and-operators.md @@ -0,0 +1,150 @@ +--- +title: Expressions and operators +--- + +Jayvee supports arbitrarily nested expressions and offers a variety of different operators. +Such expressions are similar to those used in programming languages, and they are intended to be read and understood intuitively. + +The following sections explain the definition of expressions in the language grammar, their type inference mechanism, and how the evaluation of expressions works. +As a small practical example, you may also have a look at the [arithmetics example by Langium](https://github.com/langium/langium/tree/main/examples/arithmetics) which has a heavy focus on mathematical expressions and functions. + +## Expressions in the grammar + +Expressions in the Jayvee grammar are defined in [`expression.langium`](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/grammar/expression.langium) and consist of operators (unary / binary) and literals. +Unary operators only have a single operand (e.g. the `not` operator) whereas binary operators require two operands (e.g. the `*` operator). + +The grammar is written in a way that literals end up in the leaves of the resulting AST and the nodes above represent the operators. + +As an example, have a look at the following AST structure of the expression `(-3 + 5) * 7`: + +```mermaid +classDiagram + +class `:BinaryOperator` { + operator = '*' +} + +%% Uses spaces in the name so it is unique +class ` :BinaryOperator` { + operator = '+' +} + +class `:UnaryOperator` { + operator = '-' +} + +%% Uses spaces in the name so it is unique +class ` :NumericLiteral` { + value = 3 +} + +%% Uses spaces in the name so it is unique +class ` :NumericLiteral` { + value = 5 +} + +class `:NumericLiteral` { + value = 7 +} + +`:BinaryOperator` --> ` :BinaryOperator` : leftOperand +`:BinaryOperator` --> `:NumericLiteral` : rightOperand +` :BinaryOperator` --> `:UnaryOperator` : leftOperand +`:UnaryOperator` --> ` :NumericLiteral` : operand +` :BinaryOperator` --> ` :NumericLiteral` : rightOperand +``` + +The AST is constructed in a way that ensures an unambiguous evaluation order when using depth-first search. +This implies that the AST structure already reflects the precedence and associativity of the operators, and that parentheses do not need to be explicitly represented. + +For more details on how such a grammar for expressions can be realized, see the [official Langium documentation](https://langium.org/docs/grammar-language/#tree-rewriting-actions), [this blog post](https://www.typefox.io/blog/parsing-expressions-with-xtext) and the following two sections which discuss the precedence and associativity of operators. + +### Precedence of operators + +The grammar implicitly defines the precedence of operators. +The operator precedence defines the order in which operators are evaluated within an expression. +For example, multiplication has a higher precedence than addition, which leads to multiplications being performed before additions. +In order to manually override such precedence conventions, parentheses can be used in expressions. + +The diagram below shows a more extensive precedence hierarchy, focused on common arithmetic operators. +Note that the hierarchy is arranged in ascending order, according to the operator precedence. +Such an order is similar to how operators in the actual Jayvee grammar are arranged: + +```mermaid +graph TD + subgraph BinaryOperators + add/sub(Addition Operator / Subtraction operator) --> mul/div(Multiplication Operator / Division Operator) + mul/div --> exp/root(Exponential Operator / Root Operator) + end + subgraph UnaryOperators + exp/root --> plus/minus(Plus Operator / Minus Operator) + end + plus/minus --> literal(Numeric Literal) +``` + +In the Jayvee grammar, every level of precedence is represented by a single grammar rule. +Within each such rule, the grammar rule which defines the operators with the next higher precedence is referenced. +E.g. the `AdditiveExpression` rule refers to the `MultiplicativeExpression` rule because multiplicative operators are supposed to have the next higher precedence. + +In order to alter the precedence of operators, their hierarchy of grammar rules has to be adjusted accordingly in [`expression.langium`](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/grammar/expression.langium). + +### Associativity of operators + +The associativity of operators defines the order in which operators with the same precedence are evaluated when they appear in succession without parentheses. +Operators may be either **left-associative**, **right-associative** or **non-associative**. +For example, depending on the associativity of the binary `+` operator, the expression `a + b + c` has different semantics: + +- Left-associative: `(a + b) + c` (evaluation from left to right) +- Right-associative: `a + (b + c)` (evaluation from right to left) +- Non-associative: _syntax error_, the operator cannot be chained + +The associativity of operators is also defined in the Jayvee grammar, more specifically by the structure of the grammar rules that define operators. +For details on how to encode the different kinds of associativity in grammar rules, have a look at the "Associativity" section near the end of [this blog post](https://www.typefox.io/blog/parsing-expressions-with-xtext). +The patterns described in the linked article can be found in [`expression.langium`](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/grammar/expression.langium), so each operator grammar rule encodes its own associativity. + +## Typing of expressions + +Jayvee has a mechanism for inferring and validating the type of a given expression. +This is achieved using a recursive algorithm which performs depth-first search on a given expression: + +The base case for the algorithm are literals, i.e. the tree leaves of an expression. +Depending on the type of literal, their type can be inferred trivially (in case of value literals) or otherwise from the context where the expression is located. + +For operators, the types of their operands are first inferred via recursion, and then it is checked whether they are supported by the operator. +Next, given the operand types, the resulting type is computed. +Such behavior is defined in a _type computer class_ located [here](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/lib/ast/expressions/type-computers). +Additionally, in [`operator-registry.ts`](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/lib/ast/expressions/operator-registry.ts), a type computer is registered for each kind of operator. + +In case the algorithm fails to infer a type, e.g. due to unsupported operand types or unresolved references in literals, the resulting type is `undefined`. +In order to report diagnostics in such cases, a `ValidationContext` object can be supplied when calling the type inference. + +## Evaluation of expressions + +The evaluation has the purpose of computing a single value out of an expression. +For a successful evaluation, it is a **precondition** that **a type could successfully be inferred** from the respective expression. +Also, the **value for each free variable** in that expression (i.e. literals resembling placeholders for values) needs to be **known beforehand**. +Therefore, an `ExecutionContext` object is passed to the evaluation which holds values for free variables in the current context. + +### Algorithm + +The algorithm for evaluating expressions is also based on a recursive depth-first search, similar to how the type inference works: + +Again, literals are the base case. A value literal trivially evaluates to its own value. +Other literals that resemble free variables evaluate to their value provided by the given `ExecutionContext` object. + +Regarding operators, their operands first are evaluated recursively. +Next, using the concrete operand values, the operator computes its resulting value. +This is defined in operator evaluator classes located [here](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/lib/ast/expressions/evaluators). +The classes are registered in [operator-registry.ts](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/lib/ast/expressions/operator-registry.ts) for every operator, similar to the previously mentioned type computers. + +The result of an evaluation may be `undefined` in case any errors occurred. +If the preconditions were all met, such errors are most likely arithmetic errors (like division by zero). +It is possible to provide a `ValidationContext` object to the evaluation for reporting such errors as diagnostics. + +### Evaluation strategies + +There are different evaluation strategies to choose from. +They affect the way, expressions are evaluated and thus have an impact on their semantics: + +- `exhaustive`: A full depth-first search is performed, all parts of an expression are evaluated. +- `lazy`: An evaluation with the least effort is performed. Logical operators use [short circuit semantics](https://en.wikipedia.org/wiki/Short-circuit_evaluation) and binary operators don't evaluate their right operand if the left one evaluated to `undefined`. diff --git a/apps/docs/versioned_docs/version-0.0.16/dev/09-expressions-and-operators.md.license b/apps/docs/versioned_docs/version-0.0.16/dev/09-expressions-and-operators.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/dev/09-expressions-and-operators.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.16/dev/10-debug-vs-code-extension.md b/apps/docs/versioned_docs/version-0.0.16/dev/10-debug-vs-code-extension.md new file mode 100644 index 00000000..d9ebd8ab --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/dev/10-debug-vs-code-extension.md @@ -0,0 +1,24 @@ +--- +title: Debugging via the VS Code extension +--- + +The VS Code extension of Jayvee can be used to interactively debug the language server. +During development, when using VS Code as IDE, another instance of VS Code can be opened which has the most recent, locally built VS Code extension loaded. +This can be achieved using the launch configuration `Run extension` the `Run and Debug` side-menu of VS Code or by pressing the `F5` key if that launch configuration is already selected there. + +Note that there is no file watching mechanism involved. +So in order to reflect changes to the source code, the additional VS Code instance has to be **closed and reopened** as described above. + +## How to attach a debugger + +1. Use the launch configuration `Run extension` to open the additional VS Code instance with the extension loaded +2. Use the launch configuration `Attach to Language Server` to attach the debugger + +Any set breakpoints should now be marked as active and pause the execution once they are reached. + +## How to view logs of the language server + +In the additional VS Code instance, it is possible to view the logs of the language server. +They might also be helpful for debugging since any uncaught errors are logged with their stack trace by the Langium framework. + +To view the logs, open the bottom panel called `Output` and select `Jayvee` in the dropdown menu. diff --git a/apps/docs/versioned_docs/version-0.0.16/dev/10-debug-vs-code-extension.md.license b/apps/docs/versioned_docs/version-0.0.16/dev/10-debug-vs-code-extension.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/dev/10-debug-vs-code-extension.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.16/user/block-types/ArchiveInterpreter.md b/apps/docs/versioned_docs/version-0.0.16/user/block-types/ArchiveInterpreter.md new file mode 100644 index 00000000..fcea1b68 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/block-types/ArchiveInterpreter.md @@ -0,0 +1,33 @@ +--- +title: ArchiveInterpreter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `File` + +Output type: `FileSystem` + +## Description + +Interprets a `File` as an archive file and converts it to a `FileSystem`. The archive file root is considered the root of the `FileSystem`. + +## Example 1 + +```jayvee +block ZipArchiveInterpreter oftype ArchiveInterpreter { + archiveType: "zip"; +} +``` + +Interprets a `File` as a ZIP-archive and creates a `FileSystem` of its extracted contents. + +## Properties + +### `archiveType` + +Type `text` + +#### Description + +The archive type to be interpreted, e.g., `"zip"`. diff --git a/apps/docs/versioned_docs/version-0.0.16/user/block-types/ArchiveInterpreter.md.license b/apps/docs/versioned_docs/version-0.0.16/user/block-types/ArchiveInterpreter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/block-types/ArchiveInterpreter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.16/user/block-types/CSVInterpreter.md b/apps/docs/versioned_docs/version-0.0.16/user/block-types/CSVInterpreter.md new file mode 100644 index 00000000..ca747f6e --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/block-types/CSVInterpreter.md @@ -0,0 +1,63 @@ +--- +title: CSVInterpreter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `TextFile` + +Output type: `Sheet` + +## Description + +Interprets an input file as a csv-file containing string-values delimited by `delimiter` and outputs a `Sheet`. + +## Example 1 + +```jayvee +block AgencyCSVInterpreter oftype CSVInterpreter { + delimiter: ";" + } +``` + +Interprets an input file as a csv-file containing string-values delimited by `;` and outputs `Sheet`. + +## Properties + +### `delimiter` + +Type `text` + +Default: `","` + +#### Description + +The delimiter for values in the CSV file. + +#### Example 1 + +```jayvee +delimiter: "," +``` + +Commas are used to separate values in the CSV file. + +### `enclosing` + +Type `text` + +Default: `""` + +#### Description + +The enclosing character that may be used for values in the CSV file. + +### `enclosingEscape` + +Type `text` + +Default: `""` + +#### Description + +The character to escape enclosing characters in values. diff --git a/apps/docs/versioned_docs/version-0.0.16/user/block-types/CSVInterpreter.md.license b/apps/docs/versioned_docs/version-0.0.16/user/block-types/CSVInterpreter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/block-types/CSVInterpreter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.16/user/block-types/CellRangeSelector.md b/apps/docs/versioned_docs/version-0.0.16/user/block-types/CellRangeSelector.md new file mode 100644 index 00000000..b24cef1b --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/block-types/CellRangeSelector.md @@ -0,0 +1,41 @@ +--- +title: CellRangeSelector +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Sheet` + +Output type: `Sheet` + +## Description + +Selects a subset of a `Sheet` to produce a new `Sheet`. + +## Example 1 + +```jayvee +block CarsCoreDataSelector oftype CellRangeSelector { + select: range A1:E*; +} +``` + +Selects the cells in the given range and produces a new `Sheet` containing only the selected cells. + +## Properties + +### `select` + +Type `cellRange` + +#### Description + +The cell range to select. + +#### Example 1 + +```jayvee +select: range A1:E* +``` + +Select cells from `A1` to the last cell of column `E`. diff --git a/apps/docs/versioned_docs/version-0.0.16/user/block-types/CellRangeSelector.md.license b/apps/docs/versioned_docs/version-0.0.16/user/block-types/CellRangeSelector.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/block-types/CellRangeSelector.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.16/user/block-types/CellWriter.md b/apps/docs/versioned_docs/version-0.0.16/user/block-types/CellWriter.md new file mode 100644 index 00000000..1d03a773 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/block-types/CellWriter.md @@ -0,0 +1,89 @@ +--- +title: CellWriter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Sheet` + +Output type: `Sheet` + +## Description + +Writes textual values into cells of a `Sheet`. The number of text values needs to match the number of cells to write into. + +## Example 1 + +```jayvee +block NameHeaderWriter oftype CellWriter { + at: cell A1; + write: ["Name"]; +} +``` + +Write the value "Name" into cell `A1`. + +## Example 2 + +```jayvee +block HeaderSequenceWriter oftype CellWriter { + at: range A1:A2; + write: ["Name", "Age"]; +} +``` + +Write the values "Name", "Age" into cells `A1` and `A2`. + +## Properties + +### `write` + +Type `collection<text>` + +#### Description + +The values to write. + +#### Example 1 + +```jayvee +write: ["Name"] +``` + +Write the value "Name" into the cell. + +#### Example 2 + +```jayvee +write: ["Name1", "Name2"] +``` + +Write the value "Name1" into the first cell and "Name2 into the second. + +### `at` + +Type `cellRange` + +#### Description + +The cells to write into. + +#### Validation + +Needs to be a one-dimensional range of cells. + +#### Example 1 + +```jayvee +at: cell A1 +``` + +Write into cell A1. + +#### Example 2 + +```jayvee +at: range A1:A3 +``` + +Write into cells A1, A2 and A3. diff --git a/apps/docs/versioned_docs/version-0.0.16/user/block-types/CellWriter.md.license b/apps/docs/versioned_docs/version-0.0.16/user/block-types/CellWriter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/block-types/CellWriter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.16/user/block-types/ColumnDeleter.md b/apps/docs/versioned_docs/version-0.0.16/user/block-types/ColumnDeleter.md new file mode 100644 index 00000000..73b8c37a --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/block-types/ColumnDeleter.md @@ -0,0 +1,53 @@ +--- +title: ColumnDeleter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Sheet` + +Output type: `Sheet` + +## Description + +Deletes columns from a `Sheet`. Column IDs of subsequent columns will be shifted accordingly, so there will be no gaps. + +## Example 1 + +```jayvee +block MpgColumnDeleter oftype ColumnDeleter { + delete: [column B]; +} +``` + +Deletes column B (i.e. the second column). + +## Properties + +### `delete` + +Type `collection<cellRange>` + +#### Description + +The columns to delete. + +#### Validation + +You need to specify at least one column. + +#### Example 1 + +```jayvee +delete: [column B] +``` + +Delete column B. + +#### Example 2 + +```jayvee +delete: [column B, column C] +``` + +Delete column B and column C. diff --git a/apps/docs/versioned_docs/version-0.0.16/user/block-types/ColumnDeleter.md.license b/apps/docs/versioned_docs/version-0.0.16/user/block-types/ColumnDeleter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/block-types/ColumnDeleter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.16/user/block-types/FilePicker.md b/apps/docs/versioned_docs/version-0.0.16/user/block-types/FilePicker.md new file mode 100644 index 00000000..bdc56fa5 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/block-types/FilePicker.md @@ -0,0 +1,33 @@ +--- +title: FilePicker +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `FileSystem` + +Output type: `File` + +## Description + +Selects one `File` from a `FileSystem` based on its relative path to the root of the `FileSystem`. If no file matches the relative path, no output is created and the execution of the pipeline is aborted. + +## Example 1 + +```jayvee +block AgencyFilePicker oftype FilePicker { + path: "/agency.txt"; +} +``` + +Tries to pick the file `agency.txt` from the root of the provided `FileSystem`. If `agency.txt` exists it is passed on as `File`, if it does not exist the execution of the pipeline is aborted. + +## Properties + +### `path` + +Type `text` + +#### Description + +The path of the file to select, relative to the root of the provided `FileSystem`. diff --git a/apps/docs/versioned_docs/version-0.0.16/user/block-types/FilePicker.md.license b/apps/docs/versioned_docs/version-0.0.16/user/block-types/FilePicker.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/block-types/FilePicker.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.16/user/block-types/GtfsRTInterpreter.md b/apps/docs/versioned_docs/version-0.0.16/user/block-types/GtfsRTInterpreter.md new file mode 100644 index 00000000..f92b27ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/block-types/GtfsRTInterpreter.md @@ -0,0 +1,81 @@ +--- +title: GtfsRTInterpreter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `File` + +Output type: `Sheet` + +## Description + +Interprets an protobuf file (binary) of type `File` by decoding the file according to `gtfs-realtime.proto`. Outputs the extracted entity defined by `entity` as a `Sheet` + +## Example 1 + +```jayvee +block GtfsRTTripUpdateInterpreter oftype GtfsRTInterpreter{ + entity: "trip_update"; +} +``` + +A file is interpretet as an GTFS-RT file, which contains TripUpdate. + +## Properties + +### `entity` + +Type `text` + +#### Description + +Entity to process from GTFS-RT-feed (`trip_update`, `alert` or `vehicle`). + We currently support following Output-Sheets, each are an equivalent to the flattened Element Index defined in <https://developers.google.com/transit/gtfs-realtime/reference#element-index> (just required fields are included)): + + Entity TripUpdate: + ``` + [ + 'header.gtfs_realtime_version', + 'header.timestamp', + 'header.incrementality', + 'entity.id', + 'entity.trip_update.trip.trip_id', + 'entity.trip_update.trip.route_id', + 'entity.trip_update.stop_time_update.stop_sequence', + 'entity.trip_update.stop_time_update.stop_id', + 'entity.trip_update.stop_time_update.arrival.time', + 'entity.trip_update.stop_time_update.departure.time', + ]; + + ``` + Entity VehiclePosition: + ``` + [ + 'header.gtfs_realtime_version', + 'header.timestamp', + 'header.incrementality', + 'entity.id', + 'entity.vehicle_position.vehicle_descriptor.id', + 'entity.vehicle_position.trip.trip_id', + 'entity.vehicle_position.trip.route_id', + 'entity.vehicle_position.position.latitude', + 'entity.vehicle_position.position.longitude', + 'entity.vehicle_position.timestamp', + ]; + ``` + + Entity Alert: + ``` + [ + 'header.gtfs_realtime_version', + 'header.timestamp', + 'header.incrementality', + 'entity.id', + 'entity.alert.informed_entity.route_id', + 'entity.alert.header_text', + 'entity.alert.description_text', + ]; + ``` + + diff --git a/apps/docs/versioned_docs/version-0.0.16/user/block-types/GtfsRTInterpreter.md.license b/apps/docs/versioned_docs/version-0.0.16/user/block-types/GtfsRTInterpreter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/block-types/GtfsRTInterpreter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.16/user/block-types/HttpExtractor.md b/apps/docs/versioned_docs/version-0.0.16/user/block-types/HttpExtractor.md new file mode 100644 index 00000000..611276a9 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/block-types/HttpExtractor.md @@ -0,0 +1,41 @@ +--- +title: HttpExtractor +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `None` + +Output type: `File` + +## Description + +Extracts a `File` from the web. + +## Example 1 + +```jayvee +block CarsFileExtractor oftype HttpExtractor { + url: "tinyurl.com/4ub9spwz"; +} +``` + +Fetches a file from the given URL. + +## Properties + +### `url` + +Type `text` + +#### Description + +The URL to the file in the web to extract. + +#### Example 1 + +```jayvee +url: "tinyurl.com/4ub9spwz" +``` + +Specifies the URL to fetch the data from. diff --git a/apps/docs/versioned_docs/version-0.0.16/user/block-types/HttpExtractor.md.license b/apps/docs/versioned_docs/version-0.0.16/user/block-types/HttpExtractor.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/block-types/HttpExtractor.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.16/user/block-types/PostgresLoader.md b/apps/docs/versioned_docs/version-0.0.16/user/block-types/PostgresLoader.md new file mode 100644 index 00000000..388feb20 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/block-types/PostgresLoader.md @@ -0,0 +1,78 @@ +--- +title: PostgresLoader +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Table` + +Output type: `None` + +## Description + +Loads a `Table` into a PostgreSQL database sink. + +## Example 1 + +```jayvee +block CarsLoader oftype PostgresLoader { + host: "localhost"; + port: "5432"; + username: "postgres"; + password: "postgres"; + database: "CarsDB"; + table: "Cars"; +} +``` + +A local Postgres instance is filled with table data about cars. + +## Properties + +### `host` + +Type `text` + +#### Description + +The hostname or IP address of the Postgres database. + +### `port` + +Type `integer` + +#### Description + +The port of the Postgres database. + +### `username` + +Type `text` + +#### Description + +The username to login to the Postgres database. + +### `password` + +Type `text` + +#### Description + +The password to login to the Postgres database. + +### `database` + +Type `text` + +#### Description + +The database to use. + +### `table` + +Type `text` + +#### Description + +The name of the table to write into. diff --git a/apps/docs/versioned_docs/version-0.0.16/user/block-types/PostgresLoader.md.license b/apps/docs/versioned_docs/version-0.0.16/user/block-types/PostgresLoader.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/block-types/PostgresLoader.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.16/user/block-types/RowDeleter.md b/apps/docs/versioned_docs/version-0.0.16/user/block-types/RowDeleter.md new file mode 100644 index 00000000..e7ab8483 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/block-types/RowDeleter.md @@ -0,0 +1,53 @@ +--- +title: RowDeleter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Sheet` + +Output type: `Sheet` + +## Description + +Deletes one or more rows from a `Sheet`. Row IDs of subsequent rows will be shifted accordingly, so there will be no gaps. + +## Example 1 + +```jayvee +block SecondRowDeleter oftype RowDeleter { + delete: [row 2]; +} +``` + +Deletes row 2 (i.e. the second row). + +## Properties + +### `delete` + +Type `collection<cellRange>` + +#### Description + +The rows to delete. + +#### Validation + +You need to specify at least one row. + +#### Example 1 + +```jayvee +delete: [row 2] +``` + +Delete row 2. + +#### Example 2 + +```jayvee +delete: [row 2, row 3] +``` + +Delete row 2 and row 3. diff --git a/apps/docs/versioned_docs/version-0.0.16/user/block-types/RowDeleter.md.license b/apps/docs/versioned_docs/version-0.0.16/user/block-types/RowDeleter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/block-types/RowDeleter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.16/user/block-types/SQLiteLoader.md b/apps/docs/versioned_docs/version-0.0.16/user/block-types/SQLiteLoader.md new file mode 100644 index 00000000..5a6d8133 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/block-types/SQLiteLoader.md @@ -0,0 +1,52 @@ +--- +title: SQLiteLoader +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Table` + +Output type: `None` + +## Description + +Loads a `Table` into a SQLite database sink. + +## Example 1 + +```jayvee +block CarsLoader oftype SQLiteLoader { + table: "cars"; + file: "./cars.db"; +} +``` + +A SQLite file `cars.db` is created in the working directory. Incoming data is written to the table `cars`. + +## Properties + +### `table` + +Type `text` + +#### Description + +The name of the table to write into. + +### `file` + +Type `text` + +#### Description + +The path to a SQLite file that will be created if it does not exist. Usual file extensions are `.sqlite` and `.db`. + +### `dropTable` + +Type `boolean` + +Default: `true` + +#### Description + +Indicates, whether to drop the table before loading data into it. If `false`, data is appended to the table instead of dropping it. diff --git a/apps/docs/versioned_docs/version-0.0.16/user/block-types/SQLiteLoader.md.license b/apps/docs/versioned_docs/version-0.0.16/user/block-types/SQLiteLoader.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/block-types/SQLiteLoader.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.16/user/block-types/TableInterpreter.md b/apps/docs/versioned_docs/version-0.0.16/user/block-types/TableInterpreter.md new file mode 100644 index 00000000..0c13223d --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/block-types/TableInterpreter.md @@ -0,0 +1,89 @@ +--- +title: TableInterpreter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Sheet` + +Output type: `Table` + +## Description + +Interprets a `Sheet` as a `Table`. In case a header row is present in the sheet, its names can be matched with the provided column names. Otherwise, the provided column names are assigned in order. + +## Example 1 + +```jayvee +block CarsTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "name" oftype text, + "mpg" oftype decimal, + "cyl" oftype integer, + ]; +} +``` + +Interprets a `Sheet` about cars with a topmost header row and interprets it as a `Table` by assigning a primitive valuetype to each column. The column names are matched to the header, so the order of the type assignments does not matter. + +## Example 2 + +```jayvee +block CarsTableInterpreter oftype TableInterpreter { + header: false; + columns: [ + "name" oftype text, + "mpg" oftype decimal, + "cyl" oftype integer, + ]; +} +``` + +Interprets a `Sheet` about cars without a topmost header row and interprets it as a `Table` by sequentially assigning a name and a primitive valuetype to each column of the sheet. Note that the order of columns matters here. The first column (column `A`) will be named "name", the second column (column `B`) will be named "mpg" etc. + +## Properties + +### `header` + +Type `boolean` + +#### Description + +Whether the first row should be interpreted as header row. + +#### Example 1 + +```jayvee +header: true +``` + +The first row is interpreted as table header. The values in the header row will become the column names of the table. + +#### Example 2 + +```jayvee +header: false +``` + +The first row is NOT interpreted as table header and columns of the sheet are directly mapped to table columns. The column names are taken form the provided names in the `columns` property. + +### `columns` + +Type `collection<valuetypeAssignment>` + +#### Description + +Collection of valuetype assignments. Uses column names (potentially matched with the header or by sequence depending on the `header` property) to assign a primitive valuetype to each column. + +#### Validation + +Needs to be a collection of valuetype assignments. Each column needs to have a unique name. + +#### Example 1 + +```jayvee +columns: [ "name" oftype text ] +``` + +There is one column with the header "name". All values in this colum are typed as text. diff --git a/apps/docs/versioned_docs/version-0.0.16/user/block-types/TableInterpreter.md.license b/apps/docs/versioned_docs/version-0.0.16/user/block-types/TableInterpreter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/block-types/TableInterpreter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.16/user/block-types/TableTransformer.md b/apps/docs/versioned_docs/version-0.0.16/user/block-types/TableTransformer.md new file mode 100644 index 00000000..fa52dbe6 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/block-types/TableTransformer.md @@ -0,0 +1,63 @@ +--- +title: TableTransformer +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Table` + +Output type: `Table` + +## Description + +Applies a transform on each value of a column. The input port type of the used transform has to match the type of the input column. + +## Example 1 + +```jayvee +block CelsiusToFahrenheitTransformer oftype TableTransformer { + inputColumn: 'temperature'; + outputColumn: 'temperature'; + use: CelsiusToFahrenheit; +} +``` + +Given a column "temperature" with temperature values in Celsius, it overwrites the column with computed values in Fahrenheit by using the `CelsiusToFahrenheit` transform. The transform itself is defined elsewhere in the model. + +## Example 2 + +```jayvee +block CelsiusToFahrenheitTransformer oftype TableTransformer { + inputColumn: 'temperatureCelsius'; + outputColumn: 'temperatureFahrenheit'; + use: CelsiusToFahrenheit; +} +``` + +Given a column "temperatureCelsius" with temperature values in Celsius, it adds a new column "temperatureFahrenheit" with computed values in Fahrenheit by using the `CelsiusToFahrenheit` transform. The transform itself is defined elsewhere in the model. + +## Properties + +### `inputColumn` + +Type `text` + +#### Description + +The name of the input column. Has to be present in the table and match with the transform's input port type. + +### `outputColumn` + +Type `text` + +#### Description + +The name of the output column. Overwrites the column if it already exists, or otherwise creates a new one. + +### `use` + +Type `transform` + +#### Description + +Reference to the transform that is applied to the column. diff --git a/apps/docs/versioned_docs/version-0.0.16/user/block-types/TableTransformer.md.license b/apps/docs/versioned_docs/version-0.0.16/user/block-types/TableTransformer.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/block-types/TableTransformer.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.16/user/block-types/TextFileInterpreter.md b/apps/docs/versioned_docs/version-0.0.16/user/block-types/TextFileInterpreter.md new file mode 100644 index 00000000..a25be0dc --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/block-types/TextFileInterpreter.md @@ -0,0 +1,35 @@ +--- +title: TextFileInterpreter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `File` + +Output type: `TextFile` + +## Description + +Interprets a `File` as a `TextFile`. + +## Properties + +### `encoding` + +Type `text` + +Default: `"utf-8"` + +#### Description + +The encoding used for decoding the file contents. + +### `lineBreak` + +Type `regex` + +Default: `{}` + +#### Description + +The regex for identifying line breaks. diff --git a/apps/docs/versioned_docs/version-0.0.16/user/block-types/TextFileInterpreter.md.license b/apps/docs/versioned_docs/version-0.0.16/user/block-types/TextFileInterpreter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/block-types/TextFileInterpreter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.16/user/block-types/TextLineDeleter.md b/apps/docs/versioned_docs/version-0.0.16/user/block-types/TextLineDeleter.md new file mode 100644 index 00000000..7975135f --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/block-types/TextLineDeleter.md @@ -0,0 +1,23 @@ +--- +title: TextLineDeleter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `TextFile` + +Output type: `TextFile` + +## Description + +Deletes individual lines from a `TextFile`. + +## Properties + +### `lines` + +Type `collection<integer>` + +#### Description + +The line numbers to delete. diff --git a/apps/docs/versioned_docs/version-0.0.16/user/block-types/TextLineDeleter.md.license b/apps/docs/versioned_docs/version-0.0.16/user/block-types/TextLineDeleter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/block-types/TextLineDeleter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.16/user/block-types/TextRangeSelector.md b/apps/docs/versioned_docs/version-0.0.16/user/block-types/TextRangeSelector.md new file mode 100644 index 00000000..ffdbebd0 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/block-types/TextRangeSelector.md @@ -0,0 +1,27 @@ +--- +title: TextRangeSelector +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `TextFile` + +Output type: `TextFile` + +## Description + +Selects a range of lines from a `TextFile`. + +## Properties + +### `lineFrom` + +Type `integer` + +Default: `1` + +### `lineTo` + +Type `integer` + +Default: `null` diff --git a/apps/docs/versioned_docs/version-0.0.16/user/block-types/TextRangeSelector.md.license b/apps/docs/versioned_docs/version-0.0.16/user/block-types/TextRangeSelector.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/block-types/TextRangeSelector.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.16/user/block-types/_category_.json b/apps/docs/versioned_docs/version-0.0.16/user/block-types/_category_.json new file mode 100644 index 00000000..740dd2f2 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/block-types/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Block Types", + "position": 3, + "link": { + "type": "generated-index", + "description": "These blocks are shipped with Jayvee and are available right out of the box." + } +} diff --git a/apps/docs/versioned_docs/version-0.0.16/user/block-types/_category_.json.license b/apps/docs/versioned_docs/version-0.0.16/user/block-types/_category_.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/block-types/_category_.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.16/user/constraint-types/AllowlistConstraint.md b/apps/docs/versioned_docs/version-0.0.16/user/constraint-types/AllowlistConstraint.md new file mode 100644 index 00000000..c7adab22 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/constraint-types/AllowlistConstraint.md @@ -0,0 +1,27 @@ +--- +title: AllowlistConstraint +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Compatible ValueType: text + +## Description + +Limits the values to a defined a set of allowed values. Only values in the list are valid. + +## Example 1 + +```jayvee +constraint TimeUnitString oftype AllowlistConstraint { + allowlist: ["ms", "s", "min", "h", "d", "m", "y"]; +} +``` + +Only allows the common abbreviations for millisecond, second, minute, etc.. + +## Properties + +### `allowlist` + +Type `collection<text>` diff --git a/apps/docs/versioned_docs/version-0.0.16/user/constraint-types/AllowlistConstraint.md.license b/apps/docs/versioned_docs/version-0.0.16/user/constraint-types/AllowlistConstraint.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/constraint-types/AllowlistConstraint.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.16/user/constraint-types/DenylistConstraint.md b/apps/docs/versioned_docs/version-0.0.16/user/constraint-types/DenylistConstraint.md new file mode 100644 index 00000000..ce34eab5 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/constraint-types/DenylistConstraint.md @@ -0,0 +1,27 @@ +--- +title: DenylistConstraint +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Compatible ValueType: text + +## Description + +Defines a set of forbidden values. All values in the list are considered invalid. + +## Example 1 + +```jayvee +constraint NoPrimaryColors oftype DenylistConstraint { + denylist: ["red", "blue", "yellow"]; +} +``` + +Denies all primary colors. + +## Properties + +### `denylist` + +Type `collection<text>` diff --git a/apps/docs/versioned_docs/version-0.0.16/user/constraint-types/DenylistConstraint.md.license b/apps/docs/versioned_docs/version-0.0.16/user/constraint-types/DenylistConstraint.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/constraint-types/DenylistConstraint.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.16/user/constraint-types/LengthConstraint.md b/apps/docs/versioned_docs/version-0.0.16/user/constraint-types/LengthConstraint.md new file mode 100644 index 00000000..13234897 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/constraint-types/LengthConstraint.md @@ -0,0 +1,36 @@ +--- +title: LengthConstraint +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Compatible ValueType: text + +## Description + +Limits the length of a string with an upper and/or lower boundary. Only values with a length within the given range are valid. + +## Example 1 + +```jayvee +constraint JavaStringLength oftype LengthConstraint { + minLength: 0; + maxLength: 2147483647; +} +``` + +A string with 0 to 2147483647 characters. + +## Properties + +### `minLength` + +Type `integer` + +Default: `0` + +### `maxLength` + +Type `integer` + +Default: `null` diff --git a/apps/docs/versioned_docs/version-0.0.16/user/constraint-types/LengthConstraint.md.license b/apps/docs/versioned_docs/version-0.0.16/user/constraint-types/LengthConstraint.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/constraint-types/LengthConstraint.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.16/user/constraint-types/RangeConstraint.md b/apps/docs/versioned_docs/version-0.0.16/user/constraint-types/RangeConstraint.md new file mode 100644 index 00000000..1539f2e7 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/constraint-types/RangeConstraint.md @@ -0,0 +1,60 @@ +--- +title: RangeConstraint +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Compatible ValueType: decimal + +## Description + +Limits the range of a number value with an upper and/or lower boundary which can be inclusive or exclusive. Only values within the given range are considered valid. + +## Example 1 + +```jayvee +constraint HundredScale oftype RangeConstraint { + lowerBound: 1; + upperBound: 100; +} +``` + +A scale between (and including) 1 and 100. + +## Example 2 + +```jayvee +constraint HundredScale oftype RangeConstraint { + lowerBound: 1; + lowerBoundInclusive: false; + upperBound: 100; +} +``` + +A scale for numbers strictly larger than 1 and less or equal to 100. + +## Properties + +### `lowerBound` + +Type `decimal` + +Default: `null` + +### `lowerBoundInclusive` + +Type `boolean` + +Default: `true` + +### `upperBound` + +Type `decimal` + +Default: `null` + +### `upperBoundInclusive` + +Type `boolean` + +Default: `true` diff --git a/apps/docs/versioned_docs/version-0.0.16/user/constraint-types/RangeConstraint.md.license b/apps/docs/versioned_docs/version-0.0.16/user/constraint-types/RangeConstraint.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/constraint-types/RangeConstraint.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.16/user/constraint-types/RegexConstraint.md b/apps/docs/versioned_docs/version-0.0.16/user/constraint-types/RegexConstraint.md new file mode 100644 index 00000000..68602ae2 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/constraint-types/RegexConstraint.md @@ -0,0 +1,27 @@ +--- +title: RegexConstraint +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Compatible ValueType: text + +## Description + +Limits the values complying with a regex. Only values that comply with the regex are considered valid. + +## Example 1 + +```jayvee +constraint IFOPT_Format oftype RegexConstraint { + regex: /[a-z]{2}:\d+:\d+(:\d+)?(:\d+)?/; +} +``` + +Text that complies with the IFOPT (Identification of Fixed Objects in Public Transport) DIN EN 28701:2012 format. + +## Properties + +### `regex` + +Type `regex` diff --git a/apps/docs/versioned_docs/version-0.0.16/user/constraint-types/RegexConstraint.md.license b/apps/docs/versioned_docs/version-0.0.16/user/constraint-types/RegexConstraint.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/constraint-types/RegexConstraint.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.16/user/constraint-types/_category_.json b/apps/docs/versioned_docs/version-0.0.16/user/constraint-types/_category_.json new file mode 100644 index 00000000..9cced194 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/constraint-types/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Constraint Types", + "position": 4, + "link": { + "type": "generated-index", + "description": "These constraints are shipped with Jayvee and are available right out of the box." + } +} diff --git a/apps/docs/versioned_docs/version-0.0.16/user/constraint-types/_category_.json.license b/apps/docs/versioned_docs/version-0.0.16/user/constraint-types/_category_.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/constraint-types/_category_.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.16/user/core-concepts.md b/apps/docs/versioned_docs/version-0.0.16/user/core-concepts.md new file mode 100644 index 00000000..80967458 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/core-concepts.md @@ -0,0 +1,121 @@ +--- +sidebar_position: 2 +--- + +# Core Concepts + +The core concepts of Jayvee are `Pipelines`, `Blocks`, and `ValueTypes`. + +## Pipelines + +A `Pipeline` is a sequence of different computing steps, the `Blocks`. +The default output of a block becomes the default input of the next block, building a chain of computing steps. +In the scope of a `Pipeline`, you can connect these blocks via the `pipe` syntax: + +```jayvee +pipeline CarsPipeline { + // Assumption: blocks "GasReserveHttpExtractor", "GasReserveCSVInterpreter", "GasReserveTableInterpreter", and "GasReserveLoader" are defined + + GasReserveHttpExtractor + -> GasReserveTextFileInterpreter + -> GasReserveCSVInterpreter + -> GasReserveTableInterpreter + -> GasReserveLoader; +} +``` + +Alternatively, you can use a slightly longer syntax for pipes: + +```jayvee +pipeline CarsPipeline { + // Assumption: blocks "GasReserveHttpExtractor", "GasReserveCSVInterpreter", "GasReserveTableInterpreter", and "GasReserveLoader" are defined + + pipe { + from: GasReserveHttpExtractor; + to: GasReserveTextFileInterpreter; + + } + + pipe { + from: GasReserveTextFileInterpreter; + to: GasReserveCSVInterpreter; + + } + + // etc. +} +``` + +## Blocks + +A `Block` is a processing step within a `Pipeline`. +It can have a default input and a default output. +We differentiate the following types of `Blocks`: +- `ExtractorBlocks` do not have a default input but only a default output. They model a **data source**. +- `TransformatorBlocks` have a default input and a default output. They model a **transformation**. +- `LoaderBlocks` do have a default input but nor a default output. They model a **data sink**. + +The general structure of a `Pipeline` consisting of different blocks is the following: + +```mermaid +flowchart LR + A[ExtractorBlock] --> B(TransformatorBlock) + B --> C(TransformatorBlock) + C --> D(LoaderBlock) +``` + +The common syntax of blocks is at its core a key-value map to provide configuration to the block. +The availability of property keys and their respective `ValueTypes` is determined by the type of the `Block` - indicated by the identifier after the keyword `oftype`: + +```jayvee +block GasReserveHttpExtractor oftype HttpExtractor { + // key: value + url: "https://www.bundesnetzagentur.de/_tools/SVG/js2/_functions/csv_export.html?view=renderCSV&id=1089590"; +} +``` + +In the example above, the `url` property of type `text` is defined by the corresponding `HttpExtractor` block type. + +## ValueTypes + +A `ValueType` is the definition of a data type of the processed data. +Some `Blocks` use `ValueTypes` to define logic (like filtering or assessing the data type in a data sink). +We differentiate the following types of `ValueTypes`: +- `Built-in ValueTypes` come with the basic version of Jayvee. + Currently `text`, `decimal`, `integer`, and `boolean` are supported. +- `Primitive ValueTypes` can be defined by the user to model domain-specific data types and represent a single value. + `Constraints` can be added to a `Primitive ValueType` (see [below](#constraints)). +- `Compound ValueTypes`: UPCOMING. + +### Constraints + +`Constraints` for `ValueTypes` declare the validity criteria that each concrete value is checked against. +The syntax of `Constraints` is similar to the syntax of `Blocks`. +The availability of property keys and their respective `ValueTypes` is determined by the type of the `Constraint` - indicated by the identifier after the keyword `oftype`: + +```jayvee +constraint GasFillLevelRange oftype RangeConstraint { + lowerBound: 0; + lowerBoundInclusive: true; + upperBound: 100; + upperBoundInclusive: true; +} +``` + +Note that the type of `Constraint` also determines its applicability to `ValueTypes`. +For instance, a `RangeConstraint` can only be applied to the numerical types `integer` and `decimal`. + +### Primitive ValueTypes + +`Primitive ValueTypes` are based on `Built-in ValueTypes` and use a collection of constraints to restrict the range of valid values. +Such constraints are implicitly connected via a logical `AND` relation. +Note that the `Constraints` need to be applicable to the base-type of the `ValueType` - indicated by the identifier after the keyword `oftype`: + +```jayvee +valuetype GasFillLevel oftype integer { + constraints: [ GasFillLevelRange ]; +} +``` + +### Transforms +`Transforms` are used to transform data from one `ValueType` to a different one. For more details, see [Transforms](./transforms.md) \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.16/user/core-concepts.md.license b/apps/docs/versioned_docs/version-0.0.16/user/core-concepts.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/core-concepts.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.16/user/expressions.md b/apps/docs/versioned_docs/version-0.0.16/user/expressions.md new file mode 100644 index 00000000..73400701 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/expressions.md @@ -0,0 +1,23 @@ +--- +sidebar_position: 5 +--- + +# Expressions + +Expressions in Jayvee are arbitrarily nested statements. They consist of: +- literals (e.g., numbers `5` or strings `"Example"`) +- variables (e.g., declared by `from` properties in [Transforms](./transforms.md)) +- operators (e.g., `*` or `sqrt`) + +Expressions get evaluated at runtime by the interpreter to a [Built-in ValueType](./core-concepts.md#valuetypes). + +### Example + +The following expression is evaluated to the `integer` `10`: `(2 + 3) * 2` + +The following expression is evaluated to the `integer` `3`: `floor (3.14)` + +The following expression is evaluated to the `boolean` `true`: `"Example" == "Example"` + +### Further reading +For a deeper documentation of how expressions and operators work internally, refer to the [developer docs](../dev/09-expressions-and-operators.md). \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.16/user/expressions.md.license b/apps/docs/versioned_docs/version-0.0.16/user/expressions.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/expressions.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.16/user/intro.md b/apps/docs/versioned_docs/version-0.0.16/user/intro.md new file mode 100644 index 00000000..ce7f2a82 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/intro.md @@ -0,0 +1,72 @@ +--- +sidebar_position: 1 +--- + +# Introduction to Jayvee + +Jayvee is a domain-specific language (DSL) for data engineering - the cleaning and preprocessing of data for later activities like data science or machine learning. You can use Jayvee to **model an ETL pipeline** and the command-line interpreter to **run the ETL pipeline** on your local machine. + +## Installation + +Install the interpreter via `npm`. You will need a **nodejs version >= 17.0.0**. + +```bash +npm install -g @jvalue/jayvee-interpreter +``` + +You can install a specific version using the `@`-syntax, e.g., version `0.0.16`: +```bash +npm install -g @jvalue/jayvee-interpreter@0.0.16 +``` + +## Update + +Updating the interpreter is done by reinstalling it using `npm`. Make sure to also update the [VSCode plugin](#vscode-plugin) to match the installed interpreter if you use it. + +```bash +npm install -g @jvalue/jayvee-interpreter +``` + +## Usage + +### Show help + +```console +jv -h +``` + +### Run a `.jv` file + +```console +jv <file> +``` + +Run with **additional debug output**: + +```console +jv <file> -d +``` + +With runtime parameters: + +```console +jv -e <param>=<value> -e <param>=<value> ... <file> +``` + +## Examples + +You can find multiple examples [here](https://github.com/jvalue/jayvee/tree/main/example). Copy them to your local file system and execute them with the `jv` command on your command line (see [usage](#usage)). + + +## VSCode Plugin + +To set up Jayvee locally in VS Code, you need to install the latest Jayvee VS Code extension. +To install the most recent extension, go to our [latest release](https://github.com/jvalue/jayvee/releases/latest) +and download the `jayvee.vsix` file from the release assets. +Next, go to [this page](https://code.visualstudio.com/docs/editor/extension-marketplace#_install-from-a-vsix) and +follow the instructions for installing the downloaded extension. + +## Troubleshooting + +1. Error `structuredClone is not defined` + * Please make sure you use node version 17+. diff --git a/apps/docs/versioned_docs/version-0.0.16/user/intro.md.license b/apps/docs/versioned_docs/version-0.0.16/user/intro.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/intro.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.16/user/runtime-parameters.md b/apps/docs/versioned_docs/version-0.0.16/user/runtime-parameters.md new file mode 100644 index 00000000..ac5d8995 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/runtime-parameters.md @@ -0,0 +1,27 @@ +--- +sidebar_position: 5 +--- + +# Runtime Parameters + +Property values in Jayvee can be assigned to `values` or left open for later configuration via `runtime parameters`. + +## Syntax + +Runtime parameters are indicated by the `requires` keyword, followed by the identifier of the parameter. Example + +```jayvee +block CarsLoader oftype SQLiteLoader { + table: "Cars"; + file: requires CARS_SQLITE_FILE; +} +``` + +## CLI + +The interpreter CLI has to define all existing runtime parameters for execution. +Use the CLI flag `-e` to to define them as key-value pairs of identifier and value. + +```console +jv -e <param>=<value> -e <param>=<value> ... <file> +``` diff --git a/apps/docs/versioned_docs/version-0.0.16/user/runtime-parameters.md.license b/apps/docs/versioned_docs/version-0.0.16/user/runtime-parameters.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/runtime-parameters.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.16/user/transforms.md b/apps/docs/versioned_docs/version-0.0.16/user/transforms.md new file mode 100644 index 00000000..b8312598 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/transforms.md @@ -0,0 +1,70 @@ +--- +sidebar_position: 4 +--- + +# Transforms + +Transforms are a concept in Jayvee to define the transformation of individual values. +They are similar to functions in programming languages, i.e. they perform computations on some input values and produce output values. Transform work by mapping input values to outputs using [expressions](./expressions.md). + +:::info Important + +In its current state, Jayvee only supports a single input and a single output for transforms. +For the future, it is planned to support arbitrary numbers of inputs and outputs. + +::: + +## Syntax + +The general syntax of transforms looks like this: + +```jayvee +transform <name> { + from <inputName> oftype <inputValuetype>; + to <outputName> oftype <outputValuetype>; + + <outputName>: <expression>; +} +``` + +The `transform` keyword is used to define a transform and give it a name. +The curly braces denote the body of the transform. + +The body first contains the definitions of input and output ports. +Input ports are defined using the `from` keyword whereas output ports use the `to` keyword. +Next, they are given a name and, after the `oftype` keyword, typed with a valuetype. + +Below, there needs to be an output assignment for each output port. +The output assignment defines how a particular output value is computed. +They consist of the name of an output port, followed by a `:`. +Next, an [expression](./expressions.md) specifies how the output value shall be computed. +Names of input ports can be used in such an expression to refer to input values. + +### Example + +The following transform converts temperature values from degree Celsius to Kelvin: + +```jayvee +transform CelsiusToKelvin { + from tempCelsius oftype decimal; + to tempKelvin oftype decimal; + + tempKelvin: tempCelsius + 273.15; +} +``` + +The following transform converts a text based status into a boolean value, `true` if the text is `Active`, `false` for any other value: + +```jayvee +transform StatusToBoolean { + from statusText oftype text; + to statusBoolean oftype boolean; + + statusBoolean: statusText == "Active"; +} +``` + +## Applying transforms to table columns + +Transforms can be applied to columns of a table. +Please refer to the documentation of the [`TableTransformer` block type](./block-types/TableTransformer.md) to find out how. diff --git a/apps/docs/versioned_docs/version-0.0.16/user/transforms.md.license b/apps/docs/versioned_docs/version-0.0.16/user/transforms.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.16/user/transforms.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.17/dev/01-intro.md b/apps/docs/versioned_docs/version-0.0.17/dev/01-intro.md new file mode 100644 index 00000000..c295da42 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/dev/01-intro.md @@ -0,0 +1,52 @@ +--- +sidebar_position: 1 +--- + +# Introduction for Jayvee Developers + +## How to contribute + +In order to contribute to the Jayvee project, please have a look at our [contribution guide](https://github.com/jvalue/jayvee/blob/main/CONTRIBUTING.md). + +The overall project is licensed under the `AGPL-3.0-only` license and is compliant to the [REUSE Specification](https://reuse.software/spec/) by the [Free Software Foundation Europe](https://fsfe.org/). +Because of this, contributions are required to adhere to the license and follow that specification. +More details on this topic can be found [here](./02-dev-processes/03-licensing-and-copyright.md). + +And last but not least, please read and follow our [code of conduct](https://github.com/jvalue/jayvee/blob/main/CODE_OF_CONDUCT.md). + +## Project overview + +The Jayvee repository is located [here](https://github.com/jvalue/jayvee) on GitHub. +It uses an [Nx mono-repository](https://nx.dev/) setup and contains all related projects, most notably the Jayvee language server and interpreter. +Please have a look at the [architecture overview](./03-architecture-overview.md) for more details. + +## Prerequisites + +Node.js and npm are required for development. +It is recommended to use their LTS version to avoid any potential compatibility issues. +Also, refer to this [quick start guide](https://github.com/jvalue/jayvee#development-quickstart) for developers. + +In order to run the Jayvee VS Code extension during development, VS Code is required as IDE. +Using VS Code also allows installing the [Langium VS Code extension](https://marketplace.visualstudio.com/items?itemName=langium.langium-vscode) for better IDE support when working with Langium grammar files. + +## Resources for getting started + +### Langium + +- [**Langium documentation**](https://langium.org/docs/) +- Practical examples using Langium: + - [Langium examples](https://github.com/langium/langium/tree/main/examples): Official example languages by Langium + - [Langium's grammar language](https://github.com/langium/langium/tree/main/packages/langium): The implementation of Langium's own grammar language + - [Language server for SQL](https://github.com/langium/langium-sql): An implementation of a language server for SQL + - [MiniLogo DSL](https://github.com/langium/langium-minilogo): A domain-specific language for drawing pictures + - [Lox language](https://github.com/langium/langium-lox): An implementation of the Lox scripting language (also see the "Crafting Interpreters" book below) + - [SimpleUI DSL](https://github.com/TypeFox/langium-ui-framework): A domain-specific language for generating user interfaces + - [STPA-DSL](https://github.com/kieler/stpa): A domain-specific language for System-Theoretic Process Analysis +- [Langium playground](https://langium.org/playground/): A tool for testing Langium grammars and visualizing resulting ASTs +- [Typefox blog](https://www.typefox.io/blog/): A blog by the company behind Langium, mostly posting Langium-related content. + +### Building compilers / interpreters / DSLs + +- [Crafting Interpreters](https://craftinginterpreters.com/contents.html): A book by Robert Nystrom on implementing an interpreter for the Lox scripting language +- [DSL Engineering](https://voelter.de/dslbook/markusvoelter-dslengineering-1.0.pdf): A book by Markus Voelter on designing, implementing and using domain-specific languages +- [Awesome Compilers](https://github.com/aalhour/awesome-compilers#readme): A list featuring many resources on compilers and interpreters diff --git a/apps/docs/versioned_docs/version-0.0.17/dev/01-intro.md.license b/apps/docs/versioned_docs/version-0.0.17/dev/01-intro.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/dev/01-intro.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.17/dev/02-dev-processes/01-rfc-process.md b/apps/docs/versioned_docs/version-0.0.17/dev/02-dev-processes/01-rfc-process.md new file mode 100644 index 00000000..07444085 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/dev/02-dev-processes/01-rfc-process.md @@ -0,0 +1,13 @@ +--- +title: Language Design Process (RFCs) +sidebar_position: 1 +--- + +We use RFCs to discuss changes to the language before implementing them. You can have a look at all closed (accepted / rejected) RFCs [here](https://github.com/jvalue/jayvee/pulls?q=is%3Apr+is%3Aclosed+RFC+), and all RFCs under discussion [here](https://github.com/jvalue/jayvee/pulls?q=is%3Apr+is%3Aopen+RFC). + +If you want to contribute an RFC please follow these steps: +1. Make a copy of the **template** at `rfc/0000-rfc-template.md` and place it into the `rfc` folder. +2. Create a draft for the RFC on a new branch. Follow the `TODOs` in template to do so. +3. Open a pull request with prefix `RFC <number>` in the title. +4. Address the reviews. Consider opening a new PR if major things need to be addressed and the discussion log becomes too confusing. +5. Once accepted, create an issue with the necessary steps to implement the RFC. \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.17/dev/02-dev-processes/01-rfc-process.md.license b/apps/docs/versioned_docs/version-0.0.17/dev/02-dev-processes/01-rfc-process.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/dev/02-dev-processes/01-rfc-process.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.17/dev/02-dev-processes/02-debug-vs-code-extension.md b/apps/docs/versioned_docs/version-0.0.17/dev/02-dev-processes/02-debug-vs-code-extension.md new file mode 100644 index 00000000..5fbcf07c --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/dev/02-dev-processes/02-debug-vs-code-extension.md @@ -0,0 +1,25 @@ +--- +title: Debugging via the VS Code extension +sidebar_position: 2 +--- + +The VS Code extension of Jayvee can be used to interactively debug the language server. +During development, when using VS Code as IDE, another instance of VS Code can be opened which has the most recent, locally built VS Code extension loaded. +This can be achieved using the launch configuration `Run extension` the `Run and Debug` side-menu of VS Code or by pressing the `F5` key if that launch configuration is already selected there. + +Note that there is no file watching mechanism involved. +So in order to reflect changes to the source code, the additional VS Code instance has to be **closed and reopened** as described above. + +## How to attach a debugger + +1. Use the launch configuration `Run extension` to open the additional VS Code instance with the extension loaded +2. Use the launch configuration `Attach to Language Server` to attach the debugger + +Any set breakpoints should now be marked as active and pause the execution once they are reached. + +## How to view logs of the language server + +In the additional VS Code instance, it is possible to view the logs of the language server. +They might also be helpful for debugging since any uncaught errors are logged with their stack trace by the Langium framework. + +To view the logs, open the bottom panel called `Output` and select `Jayvee` in the dropdown menu. diff --git a/apps/docs/versioned_docs/version-0.0.17/dev/02-dev-processes/02-debug-vs-code-extension.md.license b/apps/docs/versioned_docs/version-0.0.17/dev/02-dev-processes/02-debug-vs-code-extension.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/dev/02-dev-processes/02-debug-vs-code-extension.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.17/dev/02-dev-processes/03-licensing-and-copyright.md b/apps/docs/versioned_docs/version-0.0.17/dev/02-dev-processes/03-licensing-and-copyright.md new file mode 100644 index 00000000..201e21ca --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/dev/02-dev-processes/03-licensing-and-copyright.md @@ -0,0 +1,69 @@ +--- +title: Licensing and copyright +sidebar_position: 3 +--- + +The [Jayvee repository](https://github.com/jvalue/jayvee) is REUSE compliant, meaning that it fulfills the +[REUSE Specification](https://reuse.software/spec/) by the [Free Software Foundation Europe](https://fsfe.org/) (FSFE). +This makes clear under which license each project file is licensed under and who owns the copyright, not only for +humans but also for machines. + +This is done by explicitly adding copyright and licensing information to each file of the project. This is achieved +by either using a comment header or a separate `*.license` file in case comments are not possible. + +See <https://reuse.software/> more information. + +## What license is used in this project and who is the copyright holder? + +The entire project is licensed under [AGPL-3.0-only](https://spdx.org/licenses/AGPL-3.0-only.html), the +license file can be found [here](https://github.com/jvalue/jayvee/blob/main/LICENSES/AGPL-3.0-only.txt). +The copyright holder is the [Friedrich-Alexander-Universität Erlangen-Nürnberg](https://www.fau.eu/). + +## How to submit a REUSE compliant contribution? + +In case you want to contribute to the project, you will need to ensure that all of your contributed files are REUSE +compliant. In order to achieve this, you need to add a key-value pair for both copyright and licensing information +following this schema: + +``` +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only +``` + +In case a file allows comments, use single-line comments to add the copyright and licensing information at the top +of the file. Otherwise, create a corresponding `*.license` file with the text above as its content. You can have a +look at some existing project files to get an impression on it is done in practice. + +For files with common file extensions, you can use the [reuse CLI tool](https://github.com/fsfe/reuse-tool) to add +licensing and copyright information automatically. + +For more details, you can have a look at the [Getting Started tutorial](https://reuse.software/tutorial/) on the REUSE +website. + +## How to validate REUSE compliance? + +When you make a contribution and open a new pull request, the CI checks whether your contribution is REUSE compliant +using the [reuse CLI tool](https://github.com/fsfe/reuse-tool). + +In order to validate REUSE compliance in your local development environment, you have to install the +[reuse CLI tool](https://github.com/fsfe/reuse-tool) and run the following command in the projects' root folder: + +```bash +reuse lint +``` + +You can also set up a pre-commit hook, so the above command is run before each commit. +See [here](https://reuse.readthedocs.io/en/latest/readme.html#run-as-pre-commit-hook) for details on how to set it up. + +## How to hide `*.license` files in IDEs + +During development, the file explorer of your IDE may be cluttered due to the numerous `*.license` files in the +project. Luckily, most IDEs allow hiding certain files, e.g. by specifying a pattern to exclude them from the +explorer. + +Below, you can find instructions on how to hide `*.license` files in commonly used IDEs: +- **Visual Studio Code**: Add `**/*.license` to the +[`files.exclude` setting](https://code.visualstudio.com/docs/getstarted/userinterface#_explorer) +- **WebStorm**: Add `*.license` to the +[excluded files setting](https://www.jetbrains.com/help/webstorm/configuring-project-structure.html#exclude-by-pattern) diff --git a/apps/docs/versioned_docs/version-0.0.17/dev/02-dev-processes/03-licensing-and-copyright.md.license b/apps/docs/versioned_docs/version-0.0.17/dev/02-dev-processes/03-licensing-and-copyright.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/dev/02-dev-processes/03-licensing-and-copyright.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.17/dev/02-dev-processes/04-release-jayvee-version.md b/apps/docs/versioned_docs/version-0.0.17/dev/02-dev-processes/04-release-jayvee-version.md new file mode 100644 index 00000000..51630050 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/dev/02-dev-processes/04-release-jayvee-version.md @@ -0,0 +1,21 @@ +--- +title: Releasing a new Jayvee version +sidebar_position: 4 +--- + +## Version Numbers + +In this early stage of the project we do not yet follow [semantic versioning](https://semver.org/) since we expect the introduction of breaking changes frequently. +To indicate that, we only release alpha versions where the `version` is incremented with every release. +- For the npm packages, we use the version `0.0.<version>`. +- For the GitHub releases, we use the git tag `v0.0.<version>-alpha`. + +## Jayvee Release Procedure + +For releasing a new version of Jayvee, you need to complete the following steps: + +1. Increment the version in the `package.json` file. +2. Run `npx nx run docs:version-snapshot`. +3. If you are on a feature or dev branch, merge into main. +4. Create a GitHub release on the main branch. Attach a changelog. +5. The CI/CD will deal with the rest. diff --git a/apps/docs/versioned_docs/version-0.0.17/dev/02-dev-processes/04-release-jayvee-version.md.license b/apps/docs/versioned_docs/version-0.0.17/dev/02-dev-processes/04-release-jayvee-version.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/dev/02-dev-processes/04-release-jayvee-version.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.17/dev/02-dev-processes/_category_.json b/apps/docs/versioned_docs/version-0.0.17/dev/02-dev-processes/_category_.json new file mode 100644 index 00000000..c8f51245 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/dev/02-dev-processes/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Development Process", + "position": 2, + "link": { + "type": "generated-index", + "description": "Here you can find general processes around developing Jayvee." + } +} diff --git a/apps/docs/versioned_docs/version-0.0.17/dev/02-dev-processes/_category_.json.license b/apps/docs/versioned_docs/version-0.0.17/dev/02-dev-processes/_category_.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/dev/02-dev-processes/_category_.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.17/dev/03-architecture-overview.md b/apps/docs/versioned_docs/version-0.0.17/dev/03-architecture-overview.md new file mode 100644 index 00000000..cb4576fa --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/dev/03-architecture-overview.md @@ -0,0 +1,43 @@ +--- +title: Architecture overview +sidebar_position: 4 +--- + +Jayvee has a clear separation between the language itself and its execution. +This guide gives an overview of the overall architecture. + +## Language Server + +On the pure language side, the central project is the [language server](https://github.com/jvalue/jayvee/tree/main/libs/language-server) which is heavily built upon the [Langium framework](https://langium.org/). +It contains the syntax definition (i.e. the grammar) and is capable of performing static semantic analysis on models, so invalid models can be rejected and errors are reported to the user. +It uses the [Language Server Protocol](https://microsoft.github.io/language-server-protocol/) (LSP) for communicating with IDEs in order to provide common features such as diagnostics, auto completion and much more. + +## Interpreter + +The Jayvee [interpreter](https://github.com/jvalue/jayvee/tree/main/apps/interpreter) on the other hand is capable of running Jayvee models. +Therefore, it uses the language server as a library to perform lexing, parsing and semantic analysis on given models. +In case no errors were found during these phases, it executes the model by interpreting it. +This means that models are not compiled to any other language, like a compiler does, but rather directly executed on the fly without any code generation involved. +The interpreter comes with a command-line interface (CLI) for users, so they are able to execute Jayvee models easily. + +The following diagram visualizes the architecture described so far: + +```mermaid +graph LR +model[Jayvee Model] --> lexical(Lexical Analysis) +subgraph Jayvee Interpreter + subgraph Jayvee Language Server + subgraph Langium Framework + lexical --> syntax(Syntax Analysis) + end + syntax --> semantic(Semantic Analysis) + end + semantic --> interpretation(Interpretation) +end +``` + +## Jayvee Extensions + +Lastly, there are Jayvee extensions for adding additional features to Jayvee. +See [here](./04-guides/06-jayvee-extensions.md) for more details. +It is worth pointing out that extensions also follow the separation between language and execution described above. diff --git a/apps/docs/versioned_docs/version-0.0.17/dev/03-architecture-overview.md.license b/apps/docs/versioned_docs/version-0.0.17/dev/03-architecture-overview.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/dev/03-architecture-overview.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.17/dev/04-guides/01-jayvee-grammar.md b/apps/docs/versioned_docs/version-0.0.17/dev/04-guides/01-jayvee-grammar.md new file mode 100644 index 00000000..356fb09d --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/dev/04-guides/01-jayvee-grammar.md @@ -0,0 +1,34 @@ +--- +title: The Jayvee grammar +sidebar_position: 1 +--- + +The grammar of Jayvee describes the syntax and structure of the language. +It is located in the language server project. +The grammar itself is written in [Langium's own grammar language](https://langium.org/docs/grammar-language/) which is similar to [Xtext](https://www.eclipse.org/Xtext/) grammars. +Such grammar files are easily identifiable via their `.langium` file extension. + +The grammar is used to generate TypeScript interfaces that represent the Abstract Syntax Tree (AST) and different, semantically equivalent files to define syntax highlighting for different applications. +For instance, a [TextMate](https://macromates.com/manual/en/language_grammars) file is generated for the syntax highlighting in the Jayvee VS Code extension whereas a [Monarch](https://microsoft.github.io/monaco-editor/monarch.html) file is generated for the syntax highlighting in the Monaco editor. + +For further information on the grammar language of Langium, visit the corresponding [Langium documentation](https://langium.org/docs/grammar-language/). + +## Working with the grammar + +To run the code generation, either use `npm run generate` for solely the code generation or `npm run build` for an entire build. The code generation also generates further code, like the standard library. + +Whenever the grammar is changed and the code generation is run during development, it is advisory to **close and reopen the IDE**, so the changes are noticed and the file indexing is updated. + +## How to rename AST nodes + +Renaming AST nodes is not as straight forward as one might initially assume. +When renaming individual rules in the grammar, just the generated TypeScript code in `ast.ts` will reflect the renaming, but not the rest of the codebase. + +The following steps explain how to rename an AST node, so the change is reflected in the entire codebase. As an example, an AST node called `A` is supposed to be renamed to `B`: + +- Open the `ast.ts` file in the language server project +- Locate the `A` interface / type and the `isA` typeguard +- Use the rename feature by the IDE to perform the renaming from `A` to `B` and from `isA` to `isB` +- Open the grammar and locate the `A` rule +- Use the rename feature by the Langium VS Code extension to perform the renaming from `A` to `B` +- Run `npm run generate` diff --git a/apps/docs/versioned_docs/version-0.0.17/dev/04-guides/01-jayvee-grammar.md.license b/apps/docs/versioned_docs/version-0.0.17/dev/04-guides/01-jayvee-grammar.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/dev/04-guides/01-jayvee-grammar.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.17/dev/04-guides/02-working-with-the-ast.md b/apps/docs/versioned_docs/version-0.0.17/dev/04-guides/02-working-with-the-ast.md new file mode 100644 index 00000000..ca02dc33 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/dev/04-guides/02-working-with-the-ast.md @@ -0,0 +1,148 @@ +--- +title: Working with the AST +sidebar_position: 2 +--- + +The nodes of the Abstract Syntax Tree (AST) consist of types and interfaces generated from the language grammar. +See [here](./01-jayvee-grammar.md) for more information on that topic. + +The following sections provide practical guides and tips for working with nodes of the AST. + +## Dealing with potentially incomplete AST nodes + +According to the generated interfaces, properties of AST nodes are never `undefined`. +In practice however, this is not always the case. + +For example, consider the language server being confronted with an incomplete Jayvee model with syntax errors. +In such cases, properties of AST nodes are in fact `undefined`, despite their interface definition. +In the interpreter however, after lexical and syntactic analysis succeeded, it can be assumed that all AST nodes are complete (i.e. they have no undefined properties) and even that all references are resolved. + +In order to avoid accessing properties of AST nodes that are potentially undefined, it is recommended to access them via the [`?.` operator](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#optional-chaining) rather than the regular `.` operator. +Note that this might result in a conflict with the ESLint rule [`@typescript-eslint/no-unnecessary-condition`](https://typescript-eslint.io/rules/no-unnecessary-condition/), so it has to be disabled manually for such cases. + +For disabling the rule in an entire file, place this comment below the copyright and licensing header: + +```typescript +/* eslint-disable @typescript-eslint/no-unnecessary-condition */ +``` + +For just a single line, place this comment above that particular line: + +```typescript +// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition +``` + +<details> + +<summary>Full example</summary> + +Consider an exemplary AST node `A` with a property `x` of type `string`. To access that property safely: + +```typescript +import { A } from './ast' + +const astNode: A; + +// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition +const property: string | undefined = astNode?.x; +``` + +</details> + +## Usage of `assertUnreachable` + +Most times, it is beneficial to make case distinctions exhaustive, especially when working with AST nodes, properties with a [union literal type](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types) or enums. +Exhaustiveness in this context means, that the TypeScript compiler is supposed to yield an error if a case distinction does not cover all possibilities. + +Langium offers a function called `assertUnreachable` which is capable of enforcing exhaustiveness and producing compiler errors in case of violations. See the following examples to get an idea on how to use it in practice: + +<details> + +<summary>Example for an exhaustive switch statement on a union literal type</summary> + +```typescript +import { assertUnreachable } from 'langium'; + +const operator: '+' | '-'; + +switch(operator) { + case '+': { + // ... + break; + } + case '-': { + // ... + break; + } + default: { + // To ensure the switch being exhaustive on `operator`: + assertUnreachable(operator); + } +} +``` + +</details> + +<details> + +<summary>Example for an exhaustive if-elseif-else cascade using typeguards</summary> + +Consider the exemplary AST nodes `A`, `B` and `C` and that `A = B | C`: + +```typescript +import { assertUnreachable } from 'langium'; +import { A, B, isB, C, isC } from './ast' + +const astNode: A; + +if (isB(astNode)) { + // `astNode` has type `B` here +} else if (isC(astNode)) { + // `astNode` has type `C` here +} else { + // To ensure the if-elseif-else cascade being exhaustive on `astNode`: + assertUnreachable(astNode); +} +``` + +</details> + +## Usage of `assert` for expressing runtime expectations + +During development, it may occur that a certain condition is expected to **be always true** at runtime. +For example, an AST node being of a certain type or a property being defined. +The type system of TypeScript is not always able to infer such facts, so developers may have to express these expectations explicitly in their code. +Examples are [type assertions](https://www.typescriptlang.org/docs/handbook/advanced-types.html) or the [non-null assertion operator](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html#non-null-assertion-operator). +Their usage may be problematic in case the condition does not always hold, e.g. due to a bug in the program or a wrong expectation by the programmer. +In such cases, it is hard to locate the origin and debug the program because such operations are erased in the compiled JavaScript code. + +To avoid these issues, it is recommended to express such expectations as boolean expressions that are actually validated at runtime. +This can be easily achieved by using the `assert` function. +It evaluates a given boolean expression and throws an `AssertionError` in case it evaluates to `false`. +After calling `assert`, the type system of TypeScript assumes the condition to be `true` and afterwards narrows types accordingly. + +Here is an example of how to use it in practice: + +```typescript +// Import the `assert` function like this: +import { strict as assert } from 'assert'; + +import { A, B, isB } from './ast'; + +const astNode: A; +assert(isB(astNode)); +// Here `astNode` has type `B` + +const referenced = astNode?.reference?.ref; +assert(referenced !== undefined); +// Here `referred` is not `undefined` +``` + +## AST wrapper classes + +The generated interfaces for AST nodes in `ast.ts` are only meant to represent the AST structurally, they don't define any behavior. +Also, in case of syntactic sugar, there may be different kinds of AST nodes representing the same semantic language concept (e.g. single pipes with a verbose syntax or chained pipes). + +To cope with this problem, there is the concept of an `AstNodeWrapper`. +An AST node wrapper is capable of wrapping AST nodes that represent the same semantic language concept and adding behavior to them via custom methods. +To get an impression on how this can be done in practice, please have a look at the [`PipeWrapper` class](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/lib/ast/wrappers/pipe-wrapper.ts) and the [`AstNodeWrapper` interface](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/lib/ast/wrappers/ast-node-wrapper.ts). diff --git a/apps/docs/versioned_docs/version-0.0.17/dev/04-guides/02-working-with-the-ast.md.license b/apps/docs/versioned_docs/version-0.0.17/dev/04-guides/02-working-with-the-ast.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/dev/04-guides/02-working-with-the-ast.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.17/dev/04-guides/03-validation-and-diagnostics.md b/apps/docs/versioned_docs/version-0.0.17/dev/04-guides/03-validation-and-diagnostics.md new file mode 100644 index 00000000..328d5728 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/dev/04-guides/03-validation-and-diagnostics.md @@ -0,0 +1,64 @@ +--- +title: Validation and diagnostics +sidebar_position: 3 +--- + +Validation in the context of Jayvee can be understood as semantic analysis of Jayvee models. +The purpose is to uncover errors in models besides lexing, parsing and linking errors that the Langium framework already detects out of the box. +In case such errors are found, the language server makes the IDE display a diagnostic. +This means that the location of the error is underlined and an error message is shown. + +For example, each pipeline is expected to contain at least one starting block that requires no input. +Therefore, the language server analyzes every pipeline in a Jayvee model and checks for the presence of such a starting block. +In case no such block is found, the IDE underlines the pipeline definition in a red color and displays an error message. + +## How to implement validation + +### For arbitrary AST nodes + +In the file [`validation-registry.ts`](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/lib/validation/validation-registry.ts) of the language server, there is a registry containing validation functions for various AST nodes. +A validation function provided for a certain kind of AST node is called for every occurrence of such a node in a Jayvee model. +E.g. when registering a validation function for `PipelineDefinition` nodes, that function gets called once for each pipeline. + +A validation function always operates on a single, concrete AST node. +Besides the AST node, a `ValidationContext` object is passed to the function for reporting diagnostics via its `accept` method. + +The overall validation for a specific kind of AST node is usually subdivided into smaller checks that are called sequentially. +This potentially allows aborting the validation early, i.e. before all checks were run. +Aborting early may be useful if errors were reported during previous checks. +This can be determined via the `ValidationContext` which keeps track whether any errors occurred so far. + +A practical example where this plays a role are blocks: +When the type of a block is unknown to the language server, it makes no sense to validate inputs / outputs and properties of that block. +So, in case an error was reported during the check of the block type, the validation of that block is discontinued at that point. + +### For properties of blocks and constraints + +Validating property values of blocks / constraints works a bit differently because their individual validation is already incorporated into the overall validation framework. + +In general, the validation logic for a property values is located in the meta information of the block type / constraint type. +There, properties are defined using the `PropertySpecification` interface. +In order to add validation logic to a certain property, a validation function needs to be added to that property. + +For more complex validations that need to take multiple property values into account, it is also possible to provide a more general validation function which operates on the entire `PropertyBody` AST node. + +See [`text-range-selector-meta-inf.ts`](https://github.com/jvalue/jayvee/blob/main/libs/extensions/std/lang/src/text-range-selector-meta-inf.ts) for an example which includes both cases described above. + +## Reporting diagnostics + +During validation, diagnostics can be reported by calling the `accept` method of the given `ValidationContext` object. + +The call requires the severity of the diagnostic (`error`, `warning`, `info` or `hint`), the error message and its location. +The location is described by a `DiagnosticInfo` object which contains the affected AST node and optionally some further restrictions. +For more details, have a look at the [`DiagnosticInfo` interface](https://github.com/langium/langium/blob/main/packages/langium/src/validation/validation-registry.ts) by Langium. + +## Common issues + +When working on validations, a diagnostic with the following message may unexpectedly occur: `An error occurred during validation: ...` + +Such a diagnostic is generated by the Langium framework if an error object is thrown within the validation. +In most cases, the reason is the access of an `undefined` AST node property or an `AssertionError`. +For more details on such errors and how to avoid them, have a look at the documentation on [how to work with the AST](./02-working-with-the-ast.md). + +One way locate the origin of such errors is to debug the Jayvee VS Code extension, see [here](../02-dev-processes/02-debug-vs-code-extension.md) for more details. +Another possibility is to debug the interpreter and set appropriate breakpoints in the language server codebase. diff --git a/apps/docs/versioned_docs/version-0.0.17/dev/04-guides/03-validation-and-diagnostics.md.license b/apps/docs/versioned_docs/version-0.0.17/dev/04-guides/03-validation-and-diagnostics.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/dev/04-guides/03-validation-and-diagnostics.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.17/dev/04-guides/04-expressions-and-operators.md b/apps/docs/versioned_docs/version-0.0.17/dev/04-guides/04-expressions-and-operators.md new file mode 100644 index 00000000..91b51bd1 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/dev/04-guides/04-expressions-and-operators.md @@ -0,0 +1,151 @@ +--- +title: Expressions and operators +sidebar_position: 4 +--- + +Jayvee supports arbitrarily nested expressions and offers a variety of different operators. +Such expressions are similar to those used in programming languages, and they are intended to be read and understood intuitively. + +The following sections explain the definition of expressions in the language grammar, their type inference mechanism, and how the evaluation of expressions works. +As a small practical example, you may also have a look at the [arithmetics example by Langium](https://github.com/langium/langium/tree/main/examples/arithmetics) which has a heavy focus on mathematical expressions and functions. + +## Expressions in the grammar + +Expressions in the Jayvee grammar are defined in [`expression.langium`](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/grammar/expression.langium) and consist of operators (unary / binary) and literals. +Unary operators only have a single operand (e.g. the `not` operator) whereas binary operators require two operands (e.g. the `*` operator). + +The grammar is written in a way that literals end up in the leaves of the resulting AST and the nodes above represent the operators. + +As an example, have a look at the following AST structure of the expression `(-3 + 5) * 7`: + +```mermaid +classDiagram + +class `:BinaryOperator` { + operator = '*' +} + +%% Uses spaces in the name so it is unique +class ` :BinaryOperator` { + operator = '+' +} + +class `:UnaryOperator` { + operator = '-' +} + +%% Uses spaces in the name so it is unique +class ` :NumericLiteral` { + value = 3 +} + +%% Uses spaces in the name so it is unique +class ` :NumericLiteral` { + value = 5 +} + +class `:NumericLiteral` { + value = 7 +} + +`:BinaryOperator` --> ` :BinaryOperator` : leftOperand +`:BinaryOperator` --> `:NumericLiteral` : rightOperand +` :BinaryOperator` --> `:UnaryOperator` : leftOperand +`:UnaryOperator` --> ` :NumericLiteral` : operand +` :BinaryOperator` --> ` :NumericLiteral` : rightOperand +``` + +The AST is constructed in a way that ensures an unambiguous evaluation order when using depth-first search. +This implies that the AST structure already reflects the precedence and associativity of the operators, and that parentheses do not need to be explicitly represented. + +For more details on how such a grammar for expressions can be realized, see the [official Langium documentation](https://langium.org/docs/grammar-language/#tree-rewriting-actions), [this blog post](https://www.typefox.io/blog/parsing-expressions-with-xtext) and the following two sections which discuss the precedence and associativity of operators. + +### Precedence of operators + +The grammar implicitly defines the precedence of operators. +The operator precedence defines the order in which operators are evaluated within an expression. +For example, multiplication has a higher precedence than addition, which leads to multiplications being performed before additions. +In order to manually override such precedence conventions, parentheses can be used in expressions. + +The diagram below shows a more extensive precedence hierarchy, focused on common arithmetic operators. +Note that the hierarchy is arranged in ascending order, according to the operator precedence. +Such an order is similar to how operators in the actual Jayvee grammar are arranged: + +```mermaid +graph TD + subgraph BinaryOperators + add/sub(Addition Operator / Subtraction operator) --> mul/div(Multiplication Operator / Division Operator) + mul/div --> exp/root(Exponential Operator / Root Operator) + end + subgraph UnaryOperators + exp/root --> plus/minus(Plus Operator / Minus Operator) + end + plus/minus --> literal(Numeric Literal) +``` + +In the Jayvee grammar, every level of precedence is represented by a single grammar rule. +Within each such rule, the grammar rule which defines the operators with the next higher precedence is referenced. +E.g. the `AdditiveExpression` rule refers to the `MultiplicativeExpression` rule because multiplicative operators are supposed to have the next higher precedence. + +In order to alter the precedence of operators, their hierarchy of grammar rules has to be adjusted accordingly in [`expression.langium`](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/grammar/expression.langium). + +### Associativity of operators + +The associativity of operators defines the order in which operators with the same precedence are evaluated when they appear in succession without parentheses. +Operators may be either **left-associative**, **right-associative** or **non-associative**. +For example, depending on the associativity of the binary `+` operator, the expression `a + b + c` has different semantics: + +- Left-associative: `(a + b) + c` (evaluation from left to right) +- Right-associative: `a + (b + c)` (evaluation from right to left) +- Non-associative: _syntax error_, the operator cannot be chained + +The associativity of operators is also defined in the Jayvee grammar, more specifically by the structure of the grammar rules that define operators. +For details on how to encode the different kinds of associativity in grammar rules, have a look at the "Associativity" section near the end of [this blog post](https://www.typefox.io/blog/parsing-expressions-with-xtext). +The patterns described in the linked article can be found in [`expression.langium`](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/grammar/expression.langium), so each operator grammar rule encodes its own associativity. + +## Typing of expressions + +Jayvee has a mechanism for inferring and validating the type of a given expression. +This is achieved using a recursive algorithm which performs depth-first search on a given expression: + +The base case for the algorithm are literals, i.e. the tree leaves of an expression. +Depending on the type of literal, their type can be inferred trivially (in case of value literals) or otherwise from the context where the expression is located. + +For operators, the types of their operands are first inferred via recursion, and then it is checked whether they are supported by the operator. +Next, given the operand types, the resulting type is computed. +Such behavior is defined in a _type computer class_ located [here](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/lib/ast/expressions/type-computers). +Additionally, in [`operator-registry.ts`](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/lib/ast/expressions/operator-registry.ts), a type computer is registered for each kind of operator. + +In case the algorithm fails to infer a type, e.g. due to unsupported operand types or unresolved references in literals, the resulting type is `undefined`. +In order to report diagnostics in such cases, a `ValidationContext` object can be supplied when calling the type inference. + +## Evaluation of expressions + +The evaluation has the purpose of computing a single value out of an expression. +For a successful evaluation, it is a **precondition** that **a type could successfully be inferred** from the respective expression. +Also, the **value for each free variable** in that expression (i.e. literals resembling placeholders for values) needs to be **known beforehand**. +Therefore, an `ExecutionContext` object is passed to the evaluation which holds values for free variables in the current context. + +### Algorithm + +The algorithm for evaluating expressions is also based on a recursive depth-first search, similar to how the type inference works: + +Again, literals are the base case. A value literal trivially evaluates to its own value. +Other literals that resemble free variables evaluate to their value provided by the given `ExecutionContext` object. + +Regarding operators, their operands first are evaluated recursively. +Next, using the concrete operand values, the operator computes its resulting value. +This is defined in operator evaluator classes located [here](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/lib/ast/expressions/evaluators). +The classes are registered in [operator-registry.ts](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/lib/ast/expressions/operator-registry.ts) for every operator, similar to the previously mentioned type computers. + +The result of an evaluation may be `undefined` in case any errors occurred. +If the preconditions were all met, such errors are most likely arithmetic errors (like division by zero). +It is possible to provide a `ValidationContext` object to the evaluation for reporting such errors as diagnostics. + +### Evaluation strategies + +There are different evaluation strategies to choose from. +They affect the way, expressions are evaluated and thus have an impact on their semantics: + +- `exhaustive`: A full depth-first search is performed, all parts of an expression are evaluated. +- `lazy`: An evaluation with the least effort is performed. Logical operators use [short circuit semantics](https://en.wikipedia.org/wiki/Short-circuit_evaluation) and binary operators don't evaluate their right operand if the left one evaluated to `undefined`. diff --git a/apps/docs/versioned_docs/version-0.0.17/dev/04-guides/04-expressions-and-operators.md.license b/apps/docs/versioned_docs/version-0.0.17/dev/04-guides/04-expressions-and-operators.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/dev/04-guides/04-expressions-and-operators.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.17/dev/04-guides/05-standard-library.md b/apps/docs/versioned_docs/version-0.0.17/dev/04-guides/05-standard-library.md new file mode 100644 index 00000000..892e7ebe --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/dev/04-guides/05-standard-library.md @@ -0,0 +1,43 @@ +--- +title: Working with the Standard Library +sidebar_position: 5 +--- + +Jayvee ships with its own standard library on board, including the most often used valuetypes, transformations, and so on. +The standard library itself is written in `.jv` files [here](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/stdlib/). + +## Standard Library Contents + +The following elements are part of the standard library: + +## Builtin Contents +The implementations of builtin contents are not expressed in Jayvee itself but on the TypeScript layer. Examples: +- **Builtin valuetypes**: These valuetypes are the base for defining user-defined valuetypes in Jayvee, e.g., `text`, `integer`, `decimal`, `boolean`. +- **Builtin iotypes**: These iotypes are used to describe in inputs and outputs of blocktypes, e.g., `Sheet`, `File`. +- **Builtin blocktypes**: These blocktypes are the very basic building blocks in Jayvee, e.g., `HttpExtractor`, `SqliteLoader`. + +Builtin definitions are usually generated and added to the standard library from the internal representations of the concepts. + +### User-defined Contents +The implementations of user-defined contents are expressed in Jayvee itself. Examples: +- **User-defined valuetypes**: These valuetypes are based on builtin or other user-defined valuetypes. Their definition is expressed natively in Jayvee, e.g., `Percent`. +- **User-defined blocktypes**: These blocktypes are based on builtin or other user-defined blocktypes. Their definition is expressed natively in Jayvee. + +We use `jv` files to add user-defined valuetypes to the standard library (see below). + + +## Extending the Standard Library + +Just add `jv` files to the directory [here](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/stdlib/). It is crawled hierarchically, meaning that you can also organize files in folders. + +## Implementation + +### 1. Code generation + +We use code generation to transform these `.jv` files into TypeScript files that the language server can used. The [generation script](https://github.com/jvalue/jayvee/tree/main/tools/scripts/language-server/generate-stdlib.mjs) is run via `npm run generate` next to the AST generation. + +### 2. Builtin libraries + +The solution we chose to implement the standard library mechanism is close to the [builtin library tutorial](https://langium.org/guides/builtin-library/) by Langium. The following components are of interest: +- [JayveeWorkspaceManager](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/lib/builtin-library/jayvee-workspace-manager.ts) in the `language-server` that registers all libraries with the langium framework. +- [StandardLibraryFileSystemProvider](https://github.com/jvalue/jayvee/tree/main/apps/vs-code-extension/src/standard-library-file-system-provider.ts) in the `vs-code-extension` that registers all libraries with the vscode plugin framework. \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.17/dev/04-guides/05-standard-library.md.license b/apps/docs/versioned_docs/version-0.0.17/dev/04-guides/05-standard-library.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/dev/04-guides/05-standard-library.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.17/dev/04-guides/06-jayvee-extensions.md b/apps/docs/versioned_docs/version-0.0.17/dev/04-guides/06-jayvee-extensions.md new file mode 100644 index 00000000..11c34e02 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/dev/04-guides/06-jayvee-extensions.md @@ -0,0 +1,280 @@ +--- +title: Jayvee Extensions +sidebar_position: 6 +--- + +## Concepts + +### Jayvee extension + +A Jayvee extension is used to add new block types to the language without having to modify the actual grammar of the language. Such a Jayvee extension usually consists of two parts: a language extension and an execution extension which are described in the upcoming two sections. + +Jayvee extensions that shall be used by default are bundled into the so-called [standard extension](https://github.com/jvalue/jayvee/tree/main/libs/extensions/std). That way, the [language server](https://github.com/jvalue/jayvee/tree/main/libs/language-server) and the [interpreter](https://github.com/jvalue/jayvee/tree/main/apps/interpreter) are able to load them out of the box. + +### Language extension + +A language extension defines meta information of block types which are required by the +[language server](https://github.com/jvalue/jayvee/tree/main/libs/language-server). +Such meta information describes properties of +block types such as their names, input / output types and their properties. + +Note that language extensions don't define any behavior. Instead, this is done by the corresponding execution extension. + +### Execution extension + +An execution extension defines behavior for block types. They build on the meta information from the corresponding +language extension, e.g. input / output types of the block need to match the signature of the execution method and +properties are accessed by their specified name. + +Execution extensions are only required by the [interpreter](https://github.com/jvalue/jayvee/tree/main/apps/interpreter) and not necessarily by the [language server](https://github.com/jvalue/jayvee/tree/main/libs/language-server) as they solely define behavior. + +## Recipes + +### Add a new Jayvee extension + +#### 1. Generate language and execution libraries + +```bash +npx nx g @nrwl/node:library --name="extensions/<extension-name>/lang" +npx nx g @nrwl/node:library --name="extensions/<extension-name>/exec" +``` + +#### 2. Create extension classes + +In `libs/extensions/<extension-name>/lang/src/extension.ts`: + +```typescript +import { + BlockMetaInformation, + ConstructorClass, + JayveeLangExtension, +} from '@jvalue/jayvee-language-server'; + +export class MyLangExtension implements JayveeLangExtension { + getBlockMetaInf(): Array<ConstructorClass<BlockMetaInformation>> { + return []; + } +} + +``` + +In `libs/extensions/<extension-name>/exec/src/extension.ts`: + +```typescript +import { BlockExecutorClass, JayveeExecExtension } from '@jvalue/jayvee-execution'; + +export class MyExecExtension implements JayveeExecExtension { + getBlockExecutors(): BlockExecutorClass[] { + return []; + } +} +``` + +#### 3. Export extension classes + +In `libs/extensions/<extension-name>/lang/src/index.ts`: + +```typescript +export * from './extension'; +``` + +In `libs/extensions/<extension-name>/exec/src/index.ts`: + +```typescript +export * from './extension'; +``` + +#### 4. Register new extension classes in the standard extension + +In `libs/extensions/std/lang/src/extension.ts`: + +```typescript +// ... + +import { MyLangExtension } from '@jvalue/jayvee-extensions/<extension-name>/lang'; + +export class StdLangExtension implements JayveeLangExtension { + private readonly wrappedExtensions: JayveeLangExtension[] = [ + // ... + // Register your language extension here: + new MyLangExtension(), + // ... + ]; + + // ... +} +``` + +In `libs/extensions/std/exec/src/extension.ts`: + +```typescript +// ... + +import { MyExecExtension } from '@jvalue/jayvee-extensions/<extension-name>/exec'; + +export class StdExecExtension implements JayveeExecExtension { + private readonly wrappedExtensions: JayveeExecExtension[] = [ + // ... + // Register your execution extension here: + new MyExecExtension(), + // ... + ]; + + // ... +} +``` + +### Add a new block type in a Jayvee extension + +#### 1. Implement `BlockMetaInformation` + +The following example defines a block type called `MyExtractor`. This block type, for instance, takes no input and +outputs a sheet. Furthermore, it defines two properties: + +- `url` of type text +- `limit` of type integer and default value `10` + - Considered optional due to the specified default value + +In `libs/extensions/<extension-name>/lang/src/lib/my-extractor-meta-inf.ts`: + +```typescript +import { + BlockMetaInformation, + IOType, + PrimitiveValuetypes, +} from '@jvalue/jayvee-language-server'; + +export class MyExtractorMetaInformation extends BlockMetaInformation { + constructor() { + super( + // How the block type should be called: + 'MyExtractor', + + // Property definitions: + { + url: { + type: PrimitiveValuetypes.Text, + }, + limit: { + type: PrimitiveValuetypes.Integer, + defaultValue: 10, + }, + }, + + // Input type: + IOType.NONE, + + // Output type: + IOType.SHEET, + ); + } +} +``` + +> **Note** +> Use `IOType.NONE` whenever you expect no input or output: +> +> - Use it as input type if you want to define an extractor +> - Use it as output type if you want to define a loader + +#### 2. Register the new `BlockMetaInformation` in the language extension + +In `libs/extensions/<extension-name>/lang/src/extension.ts`: + +```typescript +// ... + +import { MyExtractorMetaInformation } from './lib/my-extractor-meta-inf'; + +export class MyLangExtension implements JayveeLangExtension { + getBlockMetaInf(): Array<ConstructorClass<BlockMetaInformation>> { + return [ + // ... + // Register your meta information here: + MyExtractorMetaInformation, + // ... + ]; + } +} +``` + +#### 3. Implement `BlockExecutor` + +The following example implements an executor for the previously defined block type `MyExtractor`. + +The `execute` method defines the behavior when a block is executed. Its signature matches the input and output types defined in `MyExtractorMetaInformation`. + +In `libs/extensions/<extension-name>/exec/src/lib/my-extractor-executor.ts`: + +```typescript +import * as R from '@jvalue/jayvee-execution'; +import { + AbstractBlockExecutor, + BlockExecutorClass, + ExecutionContext, + Sheet, + implementsStatic, +} from '@jvalue/jayvee-execution'; +import { IOType, PrimitiveValuetypes } from '@jvalue/jayvee-language-server'; + +@implementsStatic<BlockExecutorClass>() +export class MyExtractorExecutor + extends AbstractBlockExecutor<IOType.NONE, IOType.SHEET> +{ + // Needs to match the type in meta information: + public static readonly type = 'MyExtractor'; + + public readonly inputType = IOType.NONE; + public readonly outputType = IOType.SHEET; + + async doExecute( + input: None, + context: ExecutionContext, + ): Promise<R.Result<Sheet>> { + // Accessing property values by their name: + const url = context.getPropertyValue( + 'url', + PrimitiveValuetypes.Text, + ); + const limit = context.getPropertyValue( + 'limit', + PrimitiveValuetypes.Integer, + ); + + // ... + + if (error) { + return R.err(...); + } + + return R.ok(...); + } +} +``` + +> **Info** +> The interface `BlockExecutor<I,O>` is used as an API for block executors. The abstract class `AbstractBlockExecutor<I,O>` gives some further functionality for free, e.g., debug logging. + +> **Warning** +> The generic types of `AbstractBlockExecutor<I,O>` need to match the input and output types of the corresponding `BlockMetaInformation`. + +#### 4. Register the new `BlockExecutor` in the execution extension + +In `libs/extensions/<extension-name>/exec/src/extension.ts`: + +```typescript +// ... + +import { MyExtractorExecutor } from './lib/my-extractor-executor'; + +export class MyExecExtension implements JayveeExecExtension { + getBlockExecutors(): BlockExecutorClass[] { + return [ + // ... + // Register your block executor here: + MyExtractorExecutor, + // ... + ]; + } +} +``` diff --git a/apps/docs/versioned_docs/version-0.0.17/dev/04-guides/06-jayvee-extensions.md.license b/apps/docs/versioned_docs/version-0.0.17/dev/04-guides/06-jayvee-extensions.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/dev/04-guides/06-jayvee-extensions.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.17/dev/04-guides/_category_.json b/apps/docs/versioned_docs/version-0.0.17/dev/04-guides/_category_.json new file mode 100644 index 00000000..80f6fce8 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/dev/04-guides/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Guides", + "position": 4, + "link": { + "type": "generated-index", + "description": "Here you can find guides that will help you developing certain aspects of Jayvee." + } +} diff --git a/apps/docs/versioned_docs/version-0.0.17/dev/04-guides/_category_.json.license b/apps/docs/versioned_docs/version-0.0.17/dev/04-guides/_category_.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/dev/04-guides/_category_.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.17/user/block-types/ArchiveInterpreter.md b/apps/docs/versioned_docs/version-0.0.17/user/block-types/ArchiveInterpreter.md new file mode 100644 index 00000000..66929ee2 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/block-types/ArchiveInterpreter.md @@ -0,0 +1,33 @@ +--- +title: ArchiveInterpreter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `File` + +Output type: `FileSystem` + +## Description + +Interprets a `File` as an archive file and converts it to a `FileSystem`. The archive file root is considered the root of the `FileSystem`. + +## Example 1 + +```jayvee +block ZipArchiveInterpreter oftype ArchiveInterpreter { + archiveType: "zip"; +} +``` + +Interprets a `File` as a ZIP-archive and creates a `FileSystem` of its extracted contents. + +## Properties + +### `archiveType` + +Type `text` + +#### Description + +The archive type to be interpreted, e.g., "zip" or "gz". diff --git a/apps/docs/versioned_docs/version-0.0.17/user/block-types/ArchiveInterpreter.md.license b/apps/docs/versioned_docs/version-0.0.17/user/block-types/ArchiveInterpreter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/block-types/ArchiveInterpreter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.17/user/block-types/CSVInterpreter.md b/apps/docs/versioned_docs/version-0.0.17/user/block-types/CSVInterpreter.md new file mode 100644 index 00000000..f4a09823 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/block-types/CSVInterpreter.md @@ -0,0 +1,63 @@ +--- +title: CSVInterpreter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `TextFile` + +Output type: `Sheet` + +## Description + +Interprets an input file as a csv-file containing string-values delimited by `delimiter` and outputs a `Sheet`. + +## Example 1 + +```jayvee +block AgencyCSVInterpreter oftype CSVInterpreter { + delimiter: ";"; + } +``` + +Interprets an input file as a csv-file containing string-values delimited by `;` and outputs `Sheet`. + +## Properties + +### `delimiter` + +Type `text` + +Default: `","` + +#### Description + +The delimiter for values in the CSV file. + +#### Example 1 + +```jayvee +delimiter: "," +``` + +Commas are used to separate values in the CSV file. + +### `enclosing` + +Type `text` + +Default: `""` + +#### Description + +The enclosing character that may be used for values in the CSV file. + +### `enclosingEscape` + +Type `text` + +Default: `""` + +#### Description + +The character to escape enclosing characters in values. diff --git a/apps/docs/versioned_docs/version-0.0.17/user/block-types/CSVInterpreter.md.license b/apps/docs/versioned_docs/version-0.0.17/user/block-types/CSVInterpreter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/block-types/CSVInterpreter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.17/user/block-types/CellRangeSelector.md b/apps/docs/versioned_docs/version-0.0.17/user/block-types/CellRangeSelector.md new file mode 100644 index 00000000..075fe4a9 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/block-types/CellRangeSelector.md @@ -0,0 +1,41 @@ +--- +title: CellRangeSelector +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Sheet` + +Output type: `Sheet` + +## Description + +Selects a subset of a `Sheet` to produce a new `Sheet`. + +## Example 1 + +```jayvee +block CarsCoreDataSelector oftype CellRangeSelector { + select: range A1:E*; +} +``` + +Selects the cells in the given range and produces a new `Sheet` containing only the selected cells. + +## Properties + +### `select` + +Type `CellRange` + +#### Description + +The cell range to select. + +#### Example 1 + +```jayvee +select: range A1:E* +``` + +Select cells from `A1` to the last cell of column `E`. diff --git a/apps/docs/versioned_docs/version-0.0.17/user/block-types/CellRangeSelector.md.license b/apps/docs/versioned_docs/version-0.0.17/user/block-types/CellRangeSelector.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/block-types/CellRangeSelector.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.17/user/block-types/CellWriter.md b/apps/docs/versioned_docs/version-0.0.17/user/block-types/CellWriter.md new file mode 100644 index 00000000..fd1f5d18 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/block-types/CellWriter.md @@ -0,0 +1,89 @@ +--- +title: CellWriter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Sheet` + +Output type: `Sheet` + +## Description + +Writes textual values into cells of a `Sheet`. The number of text values needs to match the number of cells to write into. + +## Example 1 + +```jayvee +block NameHeaderWriter oftype CellWriter { + at: cell A1; + write: ["Name"]; +} +``` + +Write the value "Name" into cell `A1`. + +## Example 2 + +```jayvee +block HeaderSequenceWriter oftype CellWriter { + at: range A1:A2; + write: ["Name", "Age"]; +} +``` + +Write the values "Name", "Age" into cells `A1` and `A2`. + +## Properties + +### `write` + +Type `Collection<text>` + +#### Description + +The values to write. + +#### Example 1 + +```jayvee +write: ["Name"] +``` + +Write the value "Name" into the cell. + +#### Example 2 + +```jayvee +write: ["Name1", "Name2"] +``` + +Write the value "Name1" into the first cell and "Name2 into the second. + +### `at` + +Type `CellRange` + +#### Description + +The cells to write into. + +#### Validation + +Needs to be a one-dimensional range of cells. + +#### Example 1 + +```jayvee +at: cell A1 +``` + +Write into cell A1. + +#### Example 2 + +```jayvee +at: range A1:A3 +``` + +Write into cells A1, A2 and A3. diff --git a/apps/docs/versioned_docs/version-0.0.17/user/block-types/CellWriter.md.license b/apps/docs/versioned_docs/version-0.0.17/user/block-types/CellWriter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/block-types/CellWriter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.17/user/block-types/ColumnDeleter.md b/apps/docs/versioned_docs/version-0.0.17/user/block-types/ColumnDeleter.md new file mode 100644 index 00000000..d3b9c328 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/block-types/ColumnDeleter.md @@ -0,0 +1,53 @@ +--- +title: ColumnDeleter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Sheet` + +Output type: `Sheet` + +## Description + +Deletes columns from a `Sheet`. Column IDs of subsequent columns will be shifted accordingly, so there will be no gaps. + +## Example 1 + +```jayvee +block MpgColumnDeleter oftype ColumnDeleter { + delete: [column B]; +} +``` + +Deletes column B (i.e. the second column). + +## Properties + +### `delete` + +Type `Collection<CellRange>` + +#### Description + +The columns to delete. + +#### Validation + +You need to specify at least one column. + +#### Example 1 + +```jayvee +delete: [column B] +``` + +Delete column B. + +#### Example 2 + +```jayvee +delete: [column B, column C] +``` + +Delete column B and column C. diff --git a/apps/docs/versioned_docs/version-0.0.17/user/block-types/ColumnDeleter.md.license b/apps/docs/versioned_docs/version-0.0.17/user/block-types/ColumnDeleter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/block-types/ColumnDeleter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.17/user/block-types/FilePicker.md b/apps/docs/versioned_docs/version-0.0.17/user/block-types/FilePicker.md new file mode 100644 index 00000000..8f4a389c --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/block-types/FilePicker.md @@ -0,0 +1,33 @@ +--- +title: FilePicker +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `FileSystem` + +Output type: `File` + +## Description + +Selects one `File` from a `FileSystem` based on its relative path to the root of the `FileSystem`. If no file matches the relative path, no output is created and the execution of the pipeline is aborted. + +## Example 1 + +```jayvee +block AgencyFilePicker oftype FilePicker { + path: "./agency.txt"; +} +``` + +Tries to pick the file `agency.txt` from the root of the provided `FileSystem`. If `agency.txt` exists it is passed on as `File`, if it does not exist the execution of the pipeline is aborted. + +## Properties + +### `path` + +Type `text` + +#### Description + +The path of the file to select, relative to the root of the provided `FileSystem`. diff --git a/apps/docs/versioned_docs/version-0.0.17/user/block-types/FilePicker.md.license b/apps/docs/versioned_docs/version-0.0.17/user/block-types/FilePicker.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/block-types/FilePicker.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.17/user/block-types/GtfsRTInterpreter.md b/apps/docs/versioned_docs/version-0.0.17/user/block-types/GtfsRTInterpreter.md new file mode 100644 index 00000000..f92b27ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/block-types/GtfsRTInterpreter.md @@ -0,0 +1,81 @@ +--- +title: GtfsRTInterpreter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `File` + +Output type: `Sheet` + +## Description + +Interprets an protobuf file (binary) of type `File` by decoding the file according to `gtfs-realtime.proto`. Outputs the extracted entity defined by `entity` as a `Sheet` + +## Example 1 + +```jayvee +block GtfsRTTripUpdateInterpreter oftype GtfsRTInterpreter{ + entity: "trip_update"; +} +``` + +A file is interpretet as an GTFS-RT file, which contains TripUpdate. + +## Properties + +### `entity` + +Type `text` + +#### Description + +Entity to process from GTFS-RT-feed (`trip_update`, `alert` or `vehicle`). + We currently support following Output-Sheets, each are an equivalent to the flattened Element Index defined in <https://developers.google.com/transit/gtfs-realtime/reference#element-index> (just required fields are included)): + + Entity TripUpdate: + ``` + [ + 'header.gtfs_realtime_version', + 'header.timestamp', + 'header.incrementality', + 'entity.id', + 'entity.trip_update.trip.trip_id', + 'entity.trip_update.trip.route_id', + 'entity.trip_update.stop_time_update.stop_sequence', + 'entity.trip_update.stop_time_update.stop_id', + 'entity.trip_update.stop_time_update.arrival.time', + 'entity.trip_update.stop_time_update.departure.time', + ]; + + ``` + Entity VehiclePosition: + ``` + [ + 'header.gtfs_realtime_version', + 'header.timestamp', + 'header.incrementality', + 'entity.id', + 'entity.vehicle_position.vehicle_descriptor.id', + 'entity.vehicle_position.trip.trip_id', + 'entity.vehicle_position.trip.route_id', + 'entity.vehicle_position.position.latitude', + 'entity.vehicle_position.position.longitude', + 'entity.vehicle_position.timestamp', + ]; + ``` + + Entity Alert: + ``` + [ + 'header.gtfs_realtime_version', + 'header.timestamp', + 'header.incrementality', + 'entity.id', + 'entity.alert.informed_entity.route_id', + 'entity.alert.header_text', + 'entity.alert.description_text', + ]; + ``` + + diff --git a/apps/docs/versioned_docs/version-0.0.17/user/block-types/GtfsRTInterpreter.md.license b/apps/docs/versioned_docs/version-0.0.17/user/block-types/GtfsRTInterpreter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/block-types/GtfsRTInterpreter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.17/user/block-types/HttpExtractor.md b/apps/docs/versioned_docs/version-0.0.17/user/block-types/HttpExtractor.md new file mode 100644 index 00000000..dde1b124 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/block-types/HttpExtractor.md @@ -0,0 +1,122 @@ +--- +title: HttpExtractor +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `None` + +Output type: `File` + +## Description + +Extracts a `File` from the web. + +## Example 1 + +```jayvee +block CarsFileExtractor oftype HttpExtractor { + url: "tinyurl.com/4ub9spwz"; +} +``` + +Fetches a file from the given URL. + +## Properties + +### `url` + +Type `text` + +#### Description + +The URL to the file in the web to extract. + +#### Example 1 + +```jayvee +url: "tinyurl.com/4ub9spwz" +``` + +Specifies the URL to fetch the data from. + +### `retries` + +Type `integer` + +Default: `0` + +#### Description + +Configures how many retries should be executed after a failure fetching the data. + +#### Example 1 + +```jayvee +retries: 3 +``` + +Executes up to 3 retries if the original retry fails (so in total max. 4 requests). + +### `retryBackoffMilliseconds` + +Type `integer` + +Default: `2000` + +#### Description + +Configures the wait time in milliseconds before executing a retry. + +#### Example 1 + +```jayvee +retryBackoff: 5000 +``` + +Waits 5s (5000 ms) before executing a retry. + +### `retryBackoffStrategy` + +Type `text` + +Default: `"exponential"` + +#### Description + +Configures the wait strategy before executing a retry. Can have values "exponential" or "linear". + +#### Example 1 + +```jayvee +retryBackoffStrategy: "linear" +``` + +Waits always the same amount of time before executing a retry. + +#### Example 2 + +```jayvee +retryBackoffStrategy: "exponential" +``` + +Exponentially increases the wait time before executing a retry. + +### `followRedirects` + +Type `boolean` + +Default: `true` + +#### Description + +Indicates, whether to follow redirects on get requests. If `false`, redirects are not followed. Default `true` + +#### Example 1 + +```jayvee +url: "tinyurl.com/4ub9spwz" + followRedirects: true +``` + +Specifies the URL to fetch the data from and allows redirects. diff --git a/apps/docs/versioned_docs/version-0.0.17/user/block-types/HttpExtractor.md.license b/apps/docs/versioned_docs/version-0.0.17/user/block-types/HttpExtractor.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/block-types/HttpExtractor.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.17/user/block-types/PostgresLoader.md b/apps/docs/versioned_docs/version-0.0.17/user/block-types/PostgresLoader.md new file mode 100644 index 00000000..5dec79d9 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/block-types/PostgresLoader.md @@ -0,0 +1,78 @@ +--- +title: PostgresLoader +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Table` + +Output type: `None` + +## Description + +Loads a `Table` into a PostgreSQL database sink. + +## Example 1 + +```jayvee +block CarsLoader oftype PostgresLoader { + host: "localhost"; + port: 5432; + username: "postgres"; + password: "postgres"; + database: "CarsDB"; + table: "Cars"; +} +``` + +A local Postgres instance is filled with table data about cars. + +## Properties + +### `host` + +Type `text` + +#### Description + +The hostname or IP address of the Postgres database. + +### `port` + +Type `integer` + +#### Description + +The port of the Postgres database. + +### `username` + +Type `text` + +#### Description + +The username to login to the Postgres database. + +### `password` + +Type `text` + +#### Description + +The password to login to the Postgres database. + +### `database` + +Type `text` + +#### Description + +The database to use. + +### `table` + +Type `text` + +#### Description + +The name of the table to write into. diff --git a/apps/docs/versioned_docs/version-0.0.17/user/block-types/PostgresLoader.md.license b/apps/docs/versioned_docs/version-0.0.17/user/block-types/PostgresLoader.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/block-types/PostgresLoader.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.17/user/block-types/RowDeleter.md b/apps/docs/versioned_docs/version-0.0.17/user/block-types/RowDeleter.md new file mode 100644 index 00000000..9a2ea071 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/block-types/RowDeleter.md @@ -0,0 +1,53 @@ +--- +title: RowDeleter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Sheet` + +Output type: `Sheet` + +## Description + +Deletes one or more rows from a `Sheet`. Row IDs of subsequent rows will be shifted accordingly, so there will be no gaps. + +## Example 1 + +```jayvee +block SecondRowDeleter oftype RowDeleter { + delete: [row 2]; +} +``` + +Deletes row 2 (i.e. the second row). + +## Properties + +### `delete` + +Type `Collection<CellRange>` + +#### Description + +The rows to delete. + +#### Validation + +You need to specify at least one row. + +#### Example 1 + +```jayvee +delete: [row 2] +``` + +Delete row 2. + +#### Example 2 + +```jayvee +delete: [row 2, row 3] +``` + +Delete row 2 and row 3. diff --git a/apps/docs/versioned_docs/version-0.0.17/user/block-types/RowDeleter.md.license b/apps/docs/versioned_docs/version-0.0.17/user/block-types/RowDeleter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/block-types/RowDeleter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.17/user/block-types/SQLiteLoader.md b/apps/docs/versioned_docs/version-0.0.17/user/block-types/SQLiteLoader.md new file mode 100644 index 00000000..5a6d8133 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/block-types/SQLiteLoader.md @@ -0,0 +1,52 @@ +--- +title: SQLiteLoader +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Table` + +Output type: `None` + +## Description + +Loads a `Table` into a SQLite database sink. + +## Example 1 + +```jayvee +block CarsLoader oftype SQLiteLoader { + table: "cars"; + file: "./cars.db"; +} +``` + +A SQLite file `cars.db` is created in the working directory. Incoming data is written to the table `cars`. + +## Properties + +### `table` + +Type `text` + +#### Description + +The name of the table to write into. + +### `file` + +Type `text` + +#### Description + +The path to a SQLite file that will be created if it does not exist. Usual file extensions are `.sqlite` and `.db`. + +### `dropTable` + +Type `boolean` + +Default: `true` + +#### Description + +Indicates, whether to drop the table before loading data into it. If `false`, data is appended to the table instead of dropping it. diff --git a/apps/docs/versioned_docs/version-0.0.17/user/block-types/SQLiteLoader.md.license b/apps/docs/versioned_docs/version-0.0.17/user/block-types/SQLiteLoader.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/block-types/SQLiteLoader.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.17/user/block-types/TableInterpreter.md b/apps/docs/versioned_docs/version-0.0.17/user/block-types/TableInterpreter.md new file mode 100644 index 00000000..455cabdf --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/block-types/TableInterpreter.md @@ -0,0 +1,89 @@ +--- +title: TableInterpreter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Sheet` + +Output type: `Table` + +## Description + +Interprets a `Sheet` as a `Table`. In case a header row is present in the sheet, its names can be matched with the provided column names. Otherwise, the provided column names are assigned in order. + +## Example 1 + +```jayvee +block CarsTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "name" oftype text, + "mpg" oftype decimal, + "cyl" oftype integer, + ]; +} +``` + +Interprets a `Sheet` about cars with a topmost header row and interprets it as a `Table` by assigning a primitive valuetype to each column. The column names are matched to the header, so the order of the type assignments does not matter. + +## Example 2 + +```jayvee +block CarsTableInterpreter oftype TableInterpreter { + header: false; + columns: [ + "name" oftype text, + "mpg" oftype decimal, + "cyl" oftype integer, + ]; +} +``` + +Interprets a `Sheet` about cars without a topmost header row and interprets it as a `Table` by sequentially assigning a name and a primitive valuetype to each column of the sheet. Note that the order of columns matters here. The first column (column `A`) will be named "name", the second column (column `B`) will be named "mpg" etc. + +## Properties + +### `header` + +Type `boolean` + +#### Description + +Whether the first row should be interpreted as header row. + +#### Example 1 + +```jayvee +header: true +``` + +The first row is interpreted as table header. The values in the header row will become the column names of the table. + +#### Example 2 + +```jayvee +header: false +``` + +The first row is NOT interpreted as table header and columns of the sheet are directly mapped to table columns. The column names are taken form the provided names in the `columns` property. + +### `columns` + +Type `Collection<ValuetypeAssignment>` + +#### Description + +Collection of valuetype assignments. Uses column names (potentially matched with the header or by sequence depending on the `header` property) to assign a primitive valuetype to each column. + +#### Validation + +Needs to be a collection of valuetype assignments. Each column needs to have a unique name. + +#### Example 1 + +```jayvee +columns: [ "name" oftype text ] +``` + +There is one column with the header "name". All values in this colum are typed as text. diff --git a/apps/docs/versioned_docs/version-0.0.17/user/block-types/TableInterpreter.md.license b/apps/docs/versioned_docs/version-0.0.17/user/block-types/TableInterpreter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/block-types/TableInterpreter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.17/user/block-types/TableTransformer.md b/apps/docs/versioned_docs/version-0.0.17/user/block-types/TableTransformer.md new file mode 100644 index 00000000..54d02967 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/block-types/TableTransformer.md @@ -0,0 +1,79 @@ +--- +title: TableTransformer +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Table` + +Output type: `Table` + +## Description + +Applies a transform on each value of a column. The input port type of the used transform has to match the type of the input column. + +## Example 1 + +```jayvee + +transform CelsiusToFahrenheit { + from Celsius oftype decimal; + to Fahrenheit oftype decimal; + + Fahrenheit: (Celsius * 9/5) + 32; +} + +block CelsiusToFahrenheitTransformer oftype TableTransformer { + inputColumns: ['temperature']; + outputColumn: 'temperature'; + use: CelsiusToFahrenheit; +} +``` + +Given a column "temperature" with temperature values in Celsius, it overwrites the column with computed values in Fahrenheit by using the `CelsiusToFahrenheit` transform. The transform itself is defined elsewhere in the model. + +## Example 2 + +```jayvee + +transform CelsiusToFahrenheit { + from Celsius oftype decimal; + to Fahrenheit oftype decimal; + + Fahrenheit: (Celsius * 9/5) + 32; +} + +block CelsiusToFahrenheitTransformer oftype TableTransformer { + inputColumns: ['temperatureCelsius']; + outputColumn: 'temperatureFahrenheit'; + use: CelsiusToFahrenheit; +} +``` + +Given a column "temperatureCelsius" with temperature values in Celsius, it adds a new column "temperatureFahrenheit" with computed values in Fahrenheit by using the `CelsiusToFahrenheit` transform. The transform itself is defined elsewhere in the model. + +## Properties + +### `inputColumns` + +Type `Collection<text>` + +#### Description + +The names of the input columns. The columns have to be present in the table and match with the transform's input port types. + +### `outputColumn` + +Type `text` + +#### Description + +The name of the output column. Overwrites the column if it already exists, or otherwise creates a new one. + +### `use` + +Type `Transform` + +#### Description + +Reference to the transform that is applied to the column. diff --git a/apps/docs/versioned_docs/version-0.0.17/user/block-types/TableTransformer.md.license b/apps/docs/versioned_docs/version-0.0.17/user/block-types/TableTransformer.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/block-types/TableTransformer.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.17/user/block-types/TextFileInterpreter.md b/apps/docs/versioned_docs/version-0.0.17/user/block-types/TextFileInterpreter.md new file mode 100644 index 00000000..0fefdcc2 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/block-types/TextFileInterpreter.md @@ -0,0 +1,35 @@ +--- +title: TextFileInterpreter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `File` + +Output type: `TextFile` + +## Description + +Interprets a `File` as a `TextFile`. + +## Properties + +### `encoding` + +Type `text` + +Default: `"utf-8"` + +#### Description + +The encoding used for decoding the file contents. + +### `lineBreak` + +Type `Regex` + +Default: `{}` + +#### Description + +The regex for identifying line breaks. diff --git a/apps/docs/versioned_docs/version-0.0.17/user/block-types/TextFileInterpreter.md.license b/apps/docs/versioned_docs/version-0.0.17/user/block-types/TextFileInterpreter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/block-types/TextFileInterpreter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.17/user/block-types/TextLineDeleter.md b/apps/docs/versioned_docs/version-0.0.17/user/block-types/TextLineDeleter.md new file mode 100644 index 00000000..b36a46c4 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/block-types/TextLineDeleter.md @@ -0,0 +1,23 @@ +--- +title: TextLineDeleter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `TextFile` + +Output type: `TextFile` + +## Description + +Deletes individual lines from a `TextFile`. + +## Properties + +### `lines` + +Type `Collection<integer>` + +#### Description + +The line numbers to delete. diff --git a/apps/docs/versioned_docs/version-0.0.17/user/block-types/TextLineDeleter.md.license b/apps/docs/versioned_docs/version-0.0.17/user/block-types/TextLineDeleter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/block-types/TextLineDeleter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.17/user/block-types/TextRangeSelector.md b/apps/docs/versioned_docs/version-0.0.17/user/block-types/TextRangeSelector.md new file mode 100644 index 00000000..ffdbebd0 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/block-types/TextRangeSelector.md @@ -0,0 +1,27 @@ +--- +title: TextRangeSelector +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `TextFile` + +Output type: `TextFile` + +## Description + +Selects a range of lines from a `TextFile`. + +## Properties + +### `lineFrom` + +Type `integer` + +Default: `1` + +### `lineTo` + +Type `integer` + +Default: `null` diff --git a/apps/docs/versioned_docs/version-0.0.17/user/block-types/TextRangeSelector.md.license b/apps/docs/versioned_docs/version-0.0.17/user/block-types/TextRangeSelector.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/block-types/TextRangeSelector.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.17/user/block-types/_category_.json b/apps/docs/versioned_docs/version-0.0.17/user/block-types/_category_.json new file mode 100644 index 00000000..740dd2f2 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/block-types/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Block Types", + "position": 3, + "link": { + "type": "generated-index", + "description": "These blocks are shipped with Jayvee and are available right out of the box." + } +} diff --git a/apps/docs/versioned_docs/version-0.0.17/user/block-types/_category_.json.license b/apps/docs/versioned_docs/version-0.0.17/user/block-types/_category_.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/block-types/_category_.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.17/user/constraint-types/AllowlistConstraint.md b/apps/docs/versioned_docs/version-0.0.17/user/constraint-types/AllowlistConstraint.md new file mode 100644 index 00000000..76dd9df1 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/constraint-types/AllowlistConstraint.md @@ -0,0 +1,27 @@ +--- +title: AllowlistConstraint +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Compatible ValueType: text + +## Description + +Limits the values to a defined a set of allowed values. Only values in the list are valid. + +## Example 1 + +```jayvee +constraint TimeUnitString oftype AllowlistConstraint { + allowlist: ["ms", "s", "min", "h", "d", "m", "y"]; +} +``` + +Only allows the common abbreviations for millisecond, second, minute, etc.. + +## Properties + +### `allowlist` + +Type `Collection<text>` diff --git a/apps/docs/versioned_docs/version-0.0.17/user/constraint-types/AllowlistConstraint.md.license b/apps/docs/versioned_docs/version-0.0.17/user/constraint-types/AllowlistConstraint.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/constraint-types/AllowlistConstraint.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.17/user/constraint-types/DenylistConstraint.md b/apps/docs/versioned_docs/version-0.0.17/user/constraint-types/DenylistConstraint.md new file mode 100644 index 00000000..078f57f2 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/constraint-types/DenylistConstraint.md @@ -0,0 +1,27 @@ +--- +title: DenylistConstraint +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Compatible ValueType: text + +## Description + +Defines a set of forbidden values. All values in the list are considered invalid. + +## Example 1 + +```jayvee +constraint NoPrimaryColors oftype DenylistConstraint { + denylist: ["red", "blue", "yellow"]; +} +``` + +Denies all primary colors. + +## Properties + +### `denylist` + +Type `Collection<text>` diff --git a/apps/docs/versioned_docs/version-0.0.17/user/constraint-types/DenylistConstraint.md.license b/apps/docs/versioned_docs/version-0.0.17/user/constraint-types/DenylistConstraint.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/constraint-types/DenylistConstraint.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.17/user/constraint-types/LengthConstraint.md b/apps/docs/versioned_docs/version-0.0.17/user/constraint-types/LengthConstraint.md new file mode 100644 index 00000000..13234897 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/constraint-types/LengthConstraint.md @@ -0,0 +1,36 @@ +--- +title: LengthConstraint +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Compatible ValueType: text + +## Description + +Limits the length of a string with an upper and/or lower boundary. Only values with a length within the given range are valid. + +## Example 1 + +```jayvee +constraint JavaStringLength oftype LengthConstraint { + minLength: 0; + maxLength: 2147483647; +} +``` + +A string with 0 to 2147483647 characters. + +## Properties + +### `minLength` + +Type `integer` + +Default: `0` + +### `maxLength` + +Type `integer` + +Default: `null` diff --git a/apps/docs/versioned_docs/version-0.0.17/user/constraint-types/LengthConstraint.md.license b/apps/docs/versioned_docs/version-0.0.17/user/constraint-types/LengthConstraint.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/constraint-types/LengthConstraint.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.17/user/constraint-types/RangeConstraint.md b/apps/docs/versioned_docs/version-0.0.17/user/constraint-types/RangeConstraint.md new file mode 100644 index 00000000..1539f2e7 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/constraint-types/RangeConstraint.md @@ -0,0 +1,60 @@ +--- +title: RangeConstraint +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Compatible ValueType: decimal + +## Description + +Limits the range of a number value with an upper and/or lower boundary which can be inclusive or exclusive. Only values within the given range are considered valid. + +## Example 1 + +```jayvee +constraint HundredScale oftype RangeConstraint { + lowerBound: 1; + upperBound: 100; +} +``` + +A scale between (and including) 1 and 100. + +## Example 2 + +```jayvee +constraint HundredScale oftype RangeConstraint { + lowerBound: 1; + lowerBoundInclusive: false; + upperBound: 100; +} +``` + +A scale for numbers strictly larger than 1 and less or equal to 100. + +## Properties + +### `lowerBound` + +Type `decimal` + +Default: `null` + +### `lowerBoundInclusive` + +Type `boolean` + +Default: `true` + +### `upperBound` + +Type `decimal` + +Default: `null` + +### `upperBoundInclusive` + +Type `boolean` + +Default: `true` diff --git a/apps/docs/versioned_docs/version-0.0.17/user/constraint-types/RangeConstraint.md.license b/apps/docs/versioned_docs/version-0.0.17/user/constraint-types/RangeConstraint.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/constraint-types/RangeConstraint.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.17/user/constraint-types/RegexConstraint.md b/apps/docs/versioned_docs/version-0.0.17/user/constraint-types/RegexConstraint.md new file mode 100644 index 00000000..deb94a4b --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/constraint-types/RegexConstraint.md @@ -0,0 +1,27 @@ +--- +title: RegexConstraint +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Compatible ValueType: text + +## Description + +Limits the values complying with a regex. Only values that comply with the regex are considered valid. + +## Example 1 + +```jayvee +constraint IFOPT_Format oftype RegexConstraint { + regex: /[a-z]{2}:\d+:\d+(:\d+)?(:\d+)?/; +} +``` + +Text that complies with the IFOPT (Identification of Fixed Objects in Public Transport) DIN EN 28701:2012 format. + +## Properties + +### `regex` + +Type `Regex` diff --git a/apps/docs/versioned_docs/version-0.0.17/user/constraint-types/RegexConstraint.md.license b/apps/docs/versioned_docs/version-0.0.17/user/constraint-types/RegexConstraint.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/constraint-types/RegexConstraint.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.17/user/constraint-types/_category_.json b/apps/docs/versioned_docs/version-0.0.17/user/constraint-types/_category_.json new file mode 100644 index 00000000..7eecb5c6 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/constraint-types/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Constraint Types", + "position": 5, + "link": { + "type": "generated-index", + "description": "These constraints are shipped with Jayvee and are available right out of the box." + } +} diff --git a/apps/docs/versioned_docs/version-0.0.17/user/constraint-types/_category_.json.license b/apps/docs/versioned_docs/version-0.0.17/user/constraint-types/_category_.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/constraint-types/_category_.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.17/user/core-concepts.md b/apps/docs/versioned_docs/version-0.0.17/user/core-concepts.md new file mode 100644 index 00000000..d46c48ac --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/core-concepts.md @@ -0,0 +1,109 @@ +--- +sidebar_position: 2 +--- + +# Core Concepts + +The core concepts of Jayvee are `Pipelines`, `Blocks`, and `ValueTypes`. + +## Pipelines + +A `Pipeline` is a sequence of different computing steps, the `Blocks`. +The default output of a block becomes the default input of the next block, building a chain of computing steps. +In the scope of a `Pipeline`, you can connect these blocks via the `pipe` syntax: + +```jayvee +pipeline CarsPipeline { + // Assumption: blocks "GasReserveHttpExtractor", "GasReserveCSVInterpreter", "GasReserveTableInterpreter", and "GasReserveLoader" are defined + + GasReserveHttpExtractor + -> GasReserveTextFileInterpreter + -> GasReserveCSVInterpreter + -> GasReserveTableInterpreter + -> GasReserveLoader; +} +``` + +Alternatively, you can use a slightly longer syntax for pipes: + +```jayvee +pipeline CarsPipeline { + // Assumption: blocks "GasReserveHttpExtractor", "GasReserveCSVInterpreter", "GasReserveTableInterpreter", and "GasReserveLoader" are defined + + pipe { + from: GasReserveHttpExtractor; + to: GasReserveTextFileInterpreter; + + } + + pipe { + from: GasReserveTextFileInterpreter; + to: GasReserveCSVInterpreter; + + } + + // etc. +} +``` + +## Blocks + +A `Block` is a processing step within a `Pipeline`. +It can have a default input and a default output. +We differentiate the following types of `Blocks`: +- `ExtractorBlocks` do not have a default input but only a default output. They model a **data source**. +- `TransformatorBlocks` have a default input and a default output. They model a **transformation**. +- `LoaderBlocks` do have a default input but nor a default output. They model a **data sink**. + +The general structure of a `Pipeline` consisting of different blocks is the following: + +```mermaid +flowchart LR + A[ExtractorBlock] --> B(TransformatorBlock) + B --> C(TransformatorBlock) + C --> D(LoaderBlock) +``` + +The common syntax of blocks is at its core a key-value map to provide configuration to the block. +The availability of property keys and their respective `ValueTypes` is determined by the type of the `Block` - indicated by the identifier after the keyword `oftype`: + +```jayvee +block GasReserveHttpExtractor oftype HttpExtractor { + // key: value + url: "https://www.bundesnetzagentur.de/_tools/SVG/js2/_functions/csv_export.html?view=renderCSV&id=1089590"; +} +``` + +In the example above, the `url` property of type `text` is defined by the corresponding `HttpExtractor` block type. + +## ValueTypes + +A `ValueType` is the definition of a data type of the processed data. +Some `Blocks` use `ValueTypes` to define logic (like filtering or assessing the data type in a data sink). +We differentiate the following types of `ValueTypes`: +- `Built-in ValueTypes` come with the basic version of Jayvee. See [Built-in Valuetypes](./valuetypes/builtin-valuetypes). +- `Primitive ValueTypes` can be defined by the user to model domain-specific data types and represent a single value. + `Constraints` can be added to a `Primitive ValueType`. +See [Primitive Valuetypes](./valuetypes/primitive-valuetypes). +- `Compound ValueTypes`: UPCOMING. + +```jayvee +valuetype GasFillLevel oftype integer { + constraints: [ GasFillLevelRange ]; +} + +constraint GasFillLevelRange on decimal: + value >= 0 and value <= 100; +``` + +## Transforms +`Transforms` are used to transform data from one `ValueType` to a different one. For more details, see [Transforms](./transforms.md). + +```jayvee +transform CelsiusToKelvin { + from tempCelsius oftype decimal; + to tempKelvin oftype decimal; + + tempKelvin: tempCelsius + 273.15; +} +``` \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.17/user/core-concepts.md.license b/apps/docs/versioned_docs/version-0.0.17/user/core-concepts.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/core-concepts.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.17/user/examples/_category_.json b/apps/docs/versioned_docs/version-0.0.17/user/examples/_category_.json new file mode 100644 index 00000000..ac91a8bb --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/examples/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Jayvee Examples", + "position": 9, + "link": { + "type": "generated-index", + "description": "Examples of Jayvee models" + } +} diff --git a/apps/docs/versioned_docs/version-0.0.17/user/examples/_category_.json.license b/apps/docs/versioned_docs/version-0.0.17/user/examples/_category_.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/examples/_category_.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.17/user/examples/cars.md b/apps/docs/versioned_docs/version-0.0.17/user/examples/cars.md new file mode 100644 index 00000000..e53ede09 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/examples/cars.md @@ -0,0 +1,115 @@ +--- +title: cars +--- + +```jayvee +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// Example 1: Cars +// Learning goals: +// - Understand the core concepts pipeline, block, and pipe +// - Understand the general structure of a pipeline + +// 1. This Jayvee model describes a pipeline +// from a CSV file in the web +// to a SQLite file sink. +pipeline CarsPipeline { + + // 2. We describe the structure of the pipeline, + // usually at the top of the pipeline. + // by connecting blocks via pipes. + + // 3. Verbose syntax of a pipe + // connecting the block CarsExtractor + // with the block CarsTextFileInterpreter. + pipe { + from: CarsExtractor; + to: CarsTextFileInterpreter; + } + + // 4. The output of the "from" block is hereby used + // as input for the "to" block. + + // 5. More convenient syntax of a pipe + CarsTextFileInterpreter -> CarsCSVInterpreter; + + // 6. Pipes can be further chained, + // leading to an overview of the pipeline. + CarsCSVInterpreter + -> NameHeaderWriter + -> CarsTableInterpreter + -> CarsLoader; + + + // 7. Below the pipes, we usually define the blocks + // that are connected by the pipes. + + // 8. Blocks instantiate a blocktype by using the oftype keyword. + // The blocktype defines the available properties that the block + // can use to specify the intended behavior of the block + block CarsExtractor oftype HttpExtractor { + + // 9. Properties are assigned to concrete values. + // Here, we specify the URL where the file shall be downloaded from. + url: "https://gist.githubusercontent.com/noamross/e5d3e859aa0c794be10b/raw/b999fb4425b54c63cab088c0ce2c0d6ce961a563/cars.csv"; + } + + // 10. The HttpExtractor requires no input and produces a binary file as output. + // This file has to be interpreted, e.g., as text file. + block CarsTextFileInterpreter oftype TextFileInterpreter { } + + // 11. Next, we interpret the text file as sheet. + // A sheet only contains text cells and is useful for manipulating the shape of data before assigning more strict value types to cells. + block CarsCSVInterpreter oftype CSVInterpreter { + enclosing: '"'; + } + + // 12. We can write into cells of a sheet using the CellWriter blocktype. + block NameHeaderWriter oftype CellWriter { + // 13. We utilize a syntax similar to spreadsheet programs. + // Cell ranges can be described using the keywords "cell", "row", "column", or "range" that indicate which + // cells are selected for the write action. + at: cell A1; + + // 14. For each cell we selected with the "at" property above, + // we can specify what value shall be written into the cell. + write: ["name"]; + } + + // 15. As a next step, we interpret the sheet as a table by adding structure. + // We define a valuetype per column that specifies the data type of the column. + // Rows that include values that are not valid according to the their valuetypes are dropped automatically. + block CarsTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "name" oftype text, + "mpg" oftype decimal, + "cyl" oftype integer, + "disp" oftype decimal, + "hp" oftype integer, + "drat" oftype decimal, + "wt" oftype decimal, + "qsec" oftype decimal, + "vs" oftype integer, + "am" oftype integer, + "gear" oftype integer, + "carb" oftype integer + ]; + } + + // 16. As a last step, we load the table into a sink, + // here into a sqlite file. + // The structural information of the table is used + // to generate the correct table. + block CarsLoader oftype SQLiteLoader { + table: "Cars"; + file: "./cars.sqlite"; + } + + // 17. Congratulations! + // You can now use the sink for your data analysis, app, + // or whatever you want to do with the cleaned data. +} +``` \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.17/user/examples/cars.md.license b/apps/docs/versioned_docs/version-0.0.17/user/examples/cars.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/examples/cars.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.17/user/examples/electric-vehicles.md b/apps/docs/versioned_docs/version-0.0.17/user/examples/electric-vehicles.md new file mode 100644 index 00000000..99398633 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/examples/electric-vehicles.md @@ -0,0 +1,150 @@ +--- +title: electric-vehicles +--- + +```jayvee +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// Example 2: Electric Vehicles +// Learning goals: +// - Understand further core concepts transforms and valuetypes +// - Understand how to construct a pipeline with multiple sinks +// - Understand the use of runtime parameters + +// 1. This Jayvee model describes a pipeline +// from a CSV file in the web +// to a SQLite file and a PostgreSQL db sink. +pipeline ElectricVehiclesPipeline { + // See here for meta-data of the data source + // https://catalog.data.gov/dataset/electric-vehicle-population-data/resource/fa51be35-691f-45d2-9f3e-535877965e69 + + // 2. At the top of a pipeline, we describe the + // structure of the pipeline. The first part until + // the ElectricRangeTransformer is a linear sequence + // of blocks. From there we can see a split into two + // parallel sequences that load the data in to two + // different sinks. + ElectricVehiclesHttpExtractor + -> ElectricVehiclesTextFileInterpreter + -> ElectricVehiclesCSVInterpreter + -> ElectricVehiclesTableInterpreter + -> ElectricRangeTransformer; + + ElectricRangeTransformer + -> ElectricVehiclesSQLiteLoader; + + ElectricRangeTransformer + -> ElectricVehiclesPostgresLoader; + + // 3. After the pipeline structure, we define the blocks used. + block ElectricVehiclesHttpExtractor oftype HttpExtractor { + url: "https://data.wa.gov/api/views/f6w7-q2d2/rows.csv?accessType=DOWNLOAD"; + } + + block ElectricVehiclesTextFileInterpreter oftype TextFileInterpreter { } + + block ElectricVehiclesCSVInterpreter oftype CSVInterpreter { } + + block ElectricVehiclesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + // 4. Here, a user-deifned valuetype is used to describe this column. + // The capital letter indicates that the valuetype is not builtin + // by convention. The valuetype itself is defined further below. + "VIN (1-10)" oftype VehicleIdentificationNumber10, + "County" oftype text, + "City" oftype text, + "State" oftype UsStateCode, + "Postal Code" oftype text, + "Model Year" oftype integer, + "Make" oftype text, + "Model" oftype text, + "Electric Vehicle Type" oftype text, + "Clean Alternative Fuel Vehicle (CAFV) Eligibility" oftype text, + "Electric Range" oftype integer, + "Base MSRP" oftype integer, + "Legislative District" oftype text, + "DOL Vehicle ID" oftype integer, + "Vehicle Location" oftype text, + "Electric Utility" oftype text, + "2020 Census Tract" oftype text, + ]; + } + + // 5. This block describes the application of a transform function + // taking a column as input and adding another computed column. + // The applied transform function is defined below and referenced + // by the "use" property. + block ElectricRangeTransformer oftype TableTransformer { + inputColumns: ["Electric Range"]; + outputColumn: "Electric Range (km)"; + use: MilesToKilometers; + } + + // 6. Here, we define a transform function, taking parameters + // as input ("from" keyword), and producing an output ("to" keyword). + // Inputs and outputs have to be further described by a valuetype. + transform MilesToKilometers { + from miles oftype decimal; + to kilometers oftype integer; + + // 7. In order to express what the transform function does, + // we assign an expression to the output. Values from the input and output of the transform can be referred to by name. + kilometers: round (miles * 1.609344); + } + + block ElectricVehiclesSQLiteLoader oftype SQLiteLoader { + table: "ElectricVehiclePopulationData"; + file: "./electric-vehicles.sqlite"; + } + + block ElectricVehiclesPostgresLoader oftype PostgresLoader { + // 8. The requires keyword allows us to define runtime parameters. + // These values have to be provided as environment variables when interpreting the Jayvee model. + host: requires DB_HOST; + port: requires DB_PORT; + username: requires DB_USERNAME; + password: requires DB_PASSWORD; + database: requires DB_DATABASE; + table: "ElectricVehiclePopulationData"; + } +} + +// 9. Below the pipeline, we model user-define valuetypes. +// We give them a speaking name and provide a base valuetype +// that this valuetype builts on. User-defined valuetypes always place additional constraints on existing valuetypes. +valuetype VehicleIdentificationNumber10 oftype text { + // 10. Valuetypes can be further refined by providing constraints. + constraints: [ + OnlyCapitalLettersAndDigits, + ExactlyTenCharacters, + ]; +} + +// 11. This constraint works on text valuetypes and requires values +// to match a given regular expression in order to be valid. +constraint OnlyCapitalLettersAndDigits on text: + value matches /^[A-Z0-9]*$/; + +constraint ExactlyTenCharacters on text: + value.length == 10; + +valuetype UsStateCode oftype text { + constraints: [ + UsStateCodeAllowlist, + ]; +} + +constraint UsStateCodeAllowlist on text: + value in [ + "AL", "AK", "AZ", "AR", "AS", "CA", "CO", "CT", "DE", "DC", + "FL", "GA", "GU", "HI", "ID", "IL", "IN", "IA", "KS", "KY", + "LA", "ME", "MD", "MA", "MI", "MN", "MS", "MO", "MT", "NE", + "NV", "NH", "NJ", "NM", "NY", "NC", "ND", "MP", "OH", "OK", + "OR", "PA", "PR", "RI", "SC", "SD", "TN", "TX", "TT", "UT", + "VT", "VA", "VI", "WA", "WV", "WI", "WY", + ]; + +``` \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.17/user/examples/electric-vehicles.md.license b/apps/docs/versioned_docs/version-0.0.17/user/examples/electric-vehicles.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/examples/electric-vehicles.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.17/user/examples/gtfs-rt.md b/apps/docs/versioned_docs/version-0.0.17/user/examples/gtfs-rt.md new file mode 100644 index 00000000..7610d8c1 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/examples/gtfs-rt.md @@ -0,0 +1,133 @@ +--- +title: gtfs-rt +--- + +```jayvee +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// Example 3: GTFS Realtime Data +// Learning goals: +// - Understand the construction of a csv file with multiple tables +// - Understand how to work with live data + +// 1. This Jayvee model describes a pipeline +// from a GTFS RT data source in the web +// to a SQLite file with multiple tables. +pipeline GtfsRTSimplePipeline { + + // 2. As you can see here, we have three independent + // sequences of pipes in this pipeline. + GTFSRTTripUpdateFeedExtractor + ->GtfsRTTripUpdateInterpreter + ->TripUpdateTableInterpreter + ->TripUpdateLoader; + + GTFSRTVehiclePositionFeedExtractor + ->GtfsRTVehiclePositionInterpreter + ->VehiclePositionTableInterpreter + ->VehicleLoader; + + GTFSRTAlertFeedExtractor + ->GtfsRTAlertInterpreter + ->AlertTableInterpreter + ->AlertLoader; + + // 3. We define a series of HttpExtractors that each pull data + // from an HTTP endpoint + block GTFSRTTripUpdateFeedExtractor oftype HttpExtractor { + url: "https://proxy.transport.data.gouv.fr/resource/bibus-brest-gtfs-rt-trip-update"; + } + + block GTFSRTVehiclePositionFeedExtractor oftype HttpExtractor { + url: "https://proxy.transport.data.gouv.fr/resource/bibus-brest-gtfs-rt-vehicle-position"; + } + + block GTFSRTAlertFeedExtractor oftype HttpExtractor { + url: "https://proxy.transport.data.gouv.fr/resource/bibus-brest-gtfs-rt-alerts"; + } + + // 4. In the next step, we use the domain-specific GtfsRTInterpreter + // to interpret the fetched files as sheets + block GtfsRTTripUpdateInterpreter oftype GtfsRTInterpreter { + entity: "trip_update"; + } + + block GtfsRTAlertInterpreter oftype GtfsRTInterpreter { + entity: "alert"; + } + + block GtfsRTVehiclePositionInterpreter oftype GtfsRTInterpreter { + entity: "vehicle"; + } + + // 5. Next, we interpret the sheets as tables + block TripUpdateTableInterpreter oftype TableInterpreter { + header: true; + columns:[ + "header.gtfs_realtime_version" oftype text, + "header.timestamp" oftype text, + "header.incrementality" oftype text, + "entity.id" oftype text, + "entity.trip_update.trip.trip_id" oftype text, + "entity.trip_update.trip.route_id" oftype text, + "entity.trip_update.stop_time_update.stop_sequence" oftype text, + "entity.trip_update.stop_time_update.stop_id" oftype text, + "entity.trip_update.stop_time_update.arrival.time" oftype text, + "entity.trip_update.stop_time_update.departure.time" oftype text, + ]; + } + + block VehiclePositionTableInterpreter oftype TableInterpreter { + header: true; + columns:[ + "header.gtfs_realtime_version" oftype text, + "header.timestamp" oftype text, + "header.incrementality" oftype text, + "entity.id" oftype text, + "entity.vehicle_position.vehicle_descriptor.id" oftype text, + "entity.vehicle_position.trip.trip_id" oftype text, + "entity.vehicle_position.trip.route_id" oftype text, + "entity.vehicle_position.position.latitude" oftype text, + "entity.vehicle_position.position.longitude" oftype text, + "entity.vehicle_position.timestamp" oftype text + ]; + } + + block AlertTableInterpreter oftype TableInterpreter { + header: true; + columns:[ + 'header.gtfs_realtime_version' oftype text, + 'header.timestamp' oftype text, + 'header.incrementality' oftype text, + 'entity.id' oftype text, + 'entity.alert.informed_entity.route_id' oftype text, + 'entity.alert.header_text' oftype text, + 'entity.alert.description_text' oftype text, + ]; + } + + // 6. Last, we load the tables into the same SQLite file. + // Each loader has to define a different table name. + // For working with live data, we use the property "dropTable: false" + // to append data instead of deleting the previous data. + block TripUpdateLoader oftype SQLiteLoader { + table: "gtfs-rt-trip_update"; + file: "./gtfs.sqlite"; + dropTable: false; + } + + block VehicleLoader oftype SQLiteLoader { + table: "gtfs-rt-vehicle_position"; + file: "./gtfs.sqlite"; + dropTable: false; + } + + block AlertLoader oftype SQLiteLoader { + table: "gtfs-rt-alert"; + file: "./gtfs.sqlite"; + dropTable: false; + } +} +``` \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.17/user/examples/gtfs-rt.md.license b/apps/docs/versioned_docs/version-0.0.17/user/examples/gtfs-rt.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/examples/gtfs-rt.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.17/user/examples/gtfs-static.md b/apps/docs/versioned_docs/version-0.0.17/user/examples/gtfs-static.md new file mode 100644 index 00000000..ca6434df --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/examples/gtfs-static.md @@ -0,0 +1,370 @@ +--- +title: gtfs-static +--- + +```jayvee +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// Example 4: GTFS Static Data +// Learning goals: +// - Understand how to work with file systems + +// 1. This Jayvee model describes a pipeline +// from a zip file in the GTFS format in the web +// to a joint SQLite file with multiple tables. +pipeline GtfsPipeline { + + // 2. The origin for multiple pipe sequences is a zip + // file. Each csv file in this zip is further processed + // by its own sequence of blocks and pipes. + GTFSSampleFeedExtractor -> ZipArchiveInterpreter; + + ZipArchiveInterpreter + -> AgencyFilePicker + -> AgencyTextFileInterpreter + -> AgencyCSVInterpreter + -> AgencyTableInterpreter + -> AgencyLoader; + + ZipArchiveInterpreter + -> CalendarDatesFilePicker + -> CalendarDatesTextFileInterpreter + -> CalendarDatesCSVInterpreter + -> CalendarDatesTableInterpreter + -> CalendarDatesLoader; + + ZipArchiveInterpreter + -> CalendarFilePicker + -> CalendarTextFileInterpreter + -> CalendarCSVInterpreter + -> CalendarTableInterpreter + -> CalendarLoader; + + ZipArchiveInterpreter + -> FareAttributesFilePicker + -> FareAttributesTextFileInterpreter + -> FareAttributesCSVInterpreter + -> FareAttributesTableInterpreter + -> FareAttributesLoader; + + ZipArchiveInterpreter + -> FareRulesFilePicker + -> FareRulesTextFileInterpreter + -> FareRulesCSVInterpreter + -> FareRulesTableInterpreter + -> FareRulesLoader; + + ZipArchiveInterpreter + -> FrequenciesFilePicker + -> FrequenciesTextFileInterpreter + -> FrequenciesCSVInterpreter + -> FrequenciesTableInterpreter + -> FrequenciesLoader; + + ZipArchiveInterpreter + -> RoutesFilePicker + -> RoutesTextFileInterpreter + -> RoutesCSVInterpreter + -> RoutesTableInterpreter + -> RoutesLoader; + + ZipArchiveInterpreter + -> ShapesFilePicker + -> ShapesTextFileInterpreter + -> ShapesCSVInterpreter + -> ShapesTableInterpreter + -> ShapesLoader; + + ZipArchiveInterpreter + -> StopTimesFilePicker + -> StopTimesTextFileInterpreter + -> StopTimesCSVInterpreter + -> StopTimesTableInterpreter + -> StopTimesLoader; + + ZipArchiveInterpreter + -> StopsFilePicker + -> StopsTextFileInterpreter + -> StopsCSVInterpreter + -> StopsTableInterpreter + -> StopsLoader; + + ZipArchiveInterpreter + -> TripsFilePicker + -> TripsTextFileInterpreter + -> TripsCSVInterpreter + -> TripsTableInterpreter + -> TripsLoader; + + // 3. As a first step, we download the zip file and interpret it. + block GTFSSampleFeedExtractor oftype HttpExtractor { + url: "https://developers.google.com/static/transit/gtfs/examples/sample-feed.zip"; + } + + block ZipArchiveInterpreter oftype ArchiveInterpreter { + archiveType: "zip"; + } + + // 4. Next, we pick several csv files (with the file extension ".txt") + // for further processing . + block AgencyFilePicker oftype FilePicker { + path: "/agency.txt"; + } + + block CalendarDatesFilePicker oftype FilePicker { + path: "/calendar_dates.txt"; + } + + block CalendarFilePicker oftype FilePicker { + path: "/calendar.txt"; + } + + block FareAttributesFilePicker oftype FilePicker { + path: "/fare_attributes.txt"; + } + + block FareRulesFilePicker oftype FilePicker { + path: "/fare_rules.txt"; + } + + block FrequenciesFilePicker oftype FilePicker { + path: "/frequencies.txt"; + } + + block RoutesFilePicker oftype FilePicker { + path: "/routes.txt"; + } + + block ShapesFilePicker oftype FilePicker { + path: "/shapes.txt"; + } + + block StopTimesFilePicker oftype FilePicker { + path: "/stop_times.txt"; + } + + block StopsFilePicker oftype FilePicker { + path: "/stops.txt"; + } + + block TripsFilePicker oftype FilePicker { + path: "/trips.txt"; + } + + // 5. The rest of the pipeline follows the usual pattern. + block AgencyTextFileInterpreter oftype TextFileInterpreter { } + block CalendarDatesTextFileInterpreter oftype TextFileInterpreter { } + block CalendarTextFileInterpreter oftype TextFileInterpreter { } + block FareAttributesTextFileInterpreter oftype TextFileInterpreter { } + block FareRulesTextFileInterpreter oftype TextFileInterpreter { } + block FrequenciesTextFileInterpreter oftype TextFileInterpreter { } + block RoutesTextFileInterpreter oftype TextFileInterpreter { } + block ShapesTextFileInterpreter oftype TextFileInterpreter { } + block StopTimesTextFileInterpreter oftype TextFileInterpreter { } + block StopsTextFileInterpreter oftype TextFileInterpreter { } + block TripsTextFileInterpreter oftype TextFileInterpreter { } + block AgencyCSVInterpreter oftype CSVInterpreter { } + block CalendarDatesCSVInterpreter oftype CSVInterpreter { } + block CalendarCSVInterpreter oftype CSVInterpreter { } + block FareAttributesCSVInterpreter oftype CSVInterpreter { } + block FareRulesCSVInterpreter oftype CSVInterpreter { } + block FrequenciesCSVInterpreter oftype CSVInterpreter { } + block RoutesCSVInterpreter oftype CSVInterpreter { } + block ShapesCSVInterpreter oftype CSVInterpreter { } + block StopTimesCSVInterpreter oftype CSVInterpreter { } + block StopsCSVInterpreter oftype CSVInterpreter { } + block TripsCSVInterpreter oftype CSVInterpreter { } + + block AgencyTableInterpreter oftype TableInterpreter { + header: true; + columns:[ + "agency_id" oftype text, //Conditional columns are considered as required + "agency_name" oftype text, + "agency_url" oftype text, + "agency_timezone" oftype text + ]; + } + + block CalendarDatesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "service_id" oftype text, + "date" oftype text, + "exception_type" oftype text + ]; + } + + block CalendarTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "service_id" oftype text, + "monday" oftype text, + "tuesday" oftype text, + "wednesday" oftype text, + "thursday" oftype text, + "friday" oftype text, + "saturday" oftype text, + "sunday" oftype text, + "start_date" oftype text, + "end_date" oftype text + ]; + } + + block FareAttributesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "fare_id" oftype text, + "price" oftype text, + "currency_type" oftype text, + "payment_method" oftype text, + "transfers" oftype text, + "transfer_duration" oftype text + ]; + } + + block FareRulesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "fare_id" oftype text, + "route_id" oftype text, + "origin_id" oftype text, + "destination_id" oftype text, + "contains_id" oftype text + ]; + } + + block FrequenciesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "trip_id" oftype text, + "start_time" oftype text, + "end_time" oftype text, + "headway_secs" oftype text + ]; + } + + block RoutesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "route_id" oftype text, + "agency_id" oftype text, + "route_short_name" oftype text, + "route_long_name" oftype text, + "route_desc" oftype text, + "route_type" oftype text, + "route_url" oftype text, + "route_color" oftype text, + "route_text_color" oftype text + ]; + } + + block ShapesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "shape_id" oftype text, + "shape_pt_lat" oftype text, + "shape_pt_lon" oftype text, + "shape_pt_sequence" oftype text, + "shape_dist_traveled" oftype text + ]; + } + + block StopTimesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "trip_id" oftype text, + "arrival_time" oftype text, + "departure_time" oftype text, + "stop_id" oftype text, + "stop_sequence" oftype text, + "stop_headsign" oftype text, + "pickup_type" oftype text, + "drop_off_time" oftype text, + "shape_dist_traveled" oftype text + ]; + } + + block StopsTableInterpreter oftype TableInterpreter { + header: true; + columns:[ + "stop_id" oftype text, + "stop_name" oftype text, + "stop_desc" oftype text, + "stop_lat" oftype text, + "stop_lon" oftype text, + "zone_id" oftype text, + "stop_url" oftype text + ]; + } + + block TripsTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "route_id" oftype text, + "service_id" oftype text, + "trip_id" oftype text, + "trip_headsign" oftype text, + "direction_id" oftype text, + "block_id" oftype text, + "shape_id" oftype text + ]; + } + + block AgencyLoader oftype SQLiteLoader { + table: "agency"; + file: "./gtfs.sqlite"; + } + + block CalendarDatesLoader oftype SQLiteLoader { + table: "calendar_dates"; + file: "./gtfs.sqlite"; + } + + block CalendarLoader oftype SQLiteLoader { + table: "calendar"; + file: "./gtfs.sqlite"; + } + + block FareAttributesLoader oftype SQLiteLoader { + table: "fare_attributes"; + file: "./gtfs.sqlite"; + } + + block FareRulesLoader oftype SQLiteLoader { + table: "fare_rules"; + file: "./gtfs.sqlite"; + } + + block FrequenciesLoader oftype SQLiteLoader { + table: "frequencies"; + file: "./gtfs.sqlite"; + } + + block RoutesLoader oftype SQLiteLoader { + table: "routes"; + file: "./gtfs.sqlite"; + } + + block ShapesLoader oftype SQLiteLoader { + table: "shapes"; + file: "./gtfs.sqlite"; + } + + block StopTimesLoader oftype SQLiteLoader { + table: "stop_times"; + file: "./gtfs.sqlite"; + } + + block StopsLoader oftype SQLiteLoader { + table: "stops"; + file: "./gtfs.sqlite"; + } + + block TripsLoader oftype SQLiteLoader { + table: "trips"; + file: "./gtfs.sqlite"; + } +} +``` \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.17/user/examples/gtfs-static.md.license b/apps/docs/versioned_docs/version-0.0.17/user/examples/gtfs-static.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/examples/gtfs-static.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.17/user/expressions.md b/apps/docs/versioned_docs/version-0.0.17/user/expressions.md new file mode 100644 index 00000000..8daa73d9 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/expressions.md @@ -0,0 +1,80 @@ +--- +sidebar_position: 6 +--- + +# Expressions + +Expressions in Jayvee are arbitrarily nested statements. They consist of: +- literals (e.g., numbers `5` or strings `"Example"`) +- variables (e.g., declared by `from` properties in [Transforms](./transforms.md)) +- operators (e.g., `*` or `sqrt`) + +Expressions get evaluated at runtime by the interpreter to a [Built-in ValueType](./valuetypes/builtin-valuetypes). + +### Example + +The following expression is evaluated to the `integer` `10`: `(2 + 3) * 2` + +The following expression is evaluated to the `integer` `3`: `floor (3.14)` + +The following expression is evaluated to the `boolean` `true`: `"Example" == "Example"` + +### List of Operators + +#### Arithmetics (binary operators) +- `+` for addition, e.g., `5 + 3` evaluates to `8` +- `-` for subtraction, e.g., `5 - 3` evaluates to `2` +- `*` for multiplication, e.g., `5 * 3` evaluates to `15` +- `/` for division, e.g., `6 / 3` evaluates to `2` +- `%` for modulo, e.g., `5 % 3` evaluates to `2` +- `pow` for power, e.g., `2 pow 3` evaluates to `8` +- `root` for root, e.g., `27 root 3` evaluates to `3` + +#### Arithmetics (unary operators) +- `+` for positive signing, e.g., `+5` evaluates to `5` +- `-` for negative signing, e.g., `-5` evaluates to `-5` +- `sqrt` for square root, e.g., `sqrt 9` evaluates to `3` +- `foor` for flooring a number, e.g., `floor 5.3` evaluates to `5` +- `ceil` for ceiling a number, e.g., `floor 5.3` evaluates to `6` +- `round` for rounding a number, e.g., `floor 5.3` evaluates to `5` + +#### Relational (binary operators) +- `<` for smaller, e.g., `3 < 3` evaluates to `false` +- `<=` for smaller or equal, e.g., `3 <= 3` evaluates to `true` +- `>` for greater, e.g., `3 > 3` evaluates to `false` +- `>=` for greater or equal, e.g., `3 >= 3` evaluates to `true` +- `==` for equal, e.g., `3 == 3` evaluates to `true` +- `!=` for not equal, e.g., `3 != 3` evaluates to `false` + +#### Logical (binary operators) +- `and` for a logical and (both need to be true to evaluate to true) +- `or` for a logical or (at least left or right needs to be true to evaluate to true) +- `xor` for a logical xor (either left or right needs to be true to evaluate to true) + +#### Logical (unary operators) +- `not` for logical negation, `not true` evaluates to `false` + +#### Others (binary operators) +- `matches` for a regex match, e.g., `"A07" matches /^[A-Z0-9]*$/` evaluates to `true` +- `in` for inclusion in an array, e.g., `"a" in ["a", "b", "c"]` evaluates to `true` + +### Operator Details + +#### `in` Operator + +The `in` operator checks whether a value is included in a collection of values. For example: + +```jayvee +4.5 in [3, 6.5] // evaluates to false +3 in [3.0, 6.5] // evaluates to true +"a" in ["a", "b", "c"] // evaluates to true +``` + +The operator supports `text`, `integer` and `decimal` values as operands. The compatibility of left and right operand types follows these rules: +- For the `in` operator we have a type for the needle (left operand) and a type for the elements in the haystack (right operand). +- There is an automated type conversion as long as it is lossless and clearly defined (integer to decimal as of now). +- We allow any combination of operands that has either: (i) An automated type conversion from needle type (left operand) to the type of the elements in the haystack (right operand), or (ii) the other way around. + + +### Further reading +For a deeper documentation of how expressions and operators work internally, refer to the [developer docs](../dev/04-guides/04-expressions-and-operators.md). \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.17/user/expressions.md.license b/apps/docs/versioned_docs/version-0.0.17/user/expressions.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/expressions.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.17/user/intro.md b/apps/docs/versioned_docs/version-0.0.17/user/intro.md new file mode 100644 index 00000000..2832707c --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/intro.md @@ -0,0 +1,95 @@ +--- +sidebar_position: 1 +--- + +# Introduction to Jayvee + +Jayvee is a domain-specific language (DSL) for data engineering - the cleaning and preprocessing of data for later activities like data science or machine learning. You can use Jayvee to **model an ETL pipeline** and the command-line interpreter to **run the ETL pipeline** on your local machine. + +## Installation + +Install the interpreter via `npm`. You will need a **nodejs version >= 17.0.0**. + +```bash +npm install -g @jvalue/jayvee-interpreter +``` + +You can install a specific version using the `@`-syntax, e.g., version `0.0.17`: +```bash +npm install -g @jvalue/jayvee-interpreter@0.0.17 +``` + +## Update + +Updating the interpreter is done by reinstalling it using `npm`. Make sure to also update the [VSCode plugin](#vscode-plugin) to match the installed interpreter if you use it. + +```bash +npm install -g @jvalue/jayvee-interpreter +``` + +## Usage + +### Show help + +```console +jv -h +``` + +### Run a `.jv` file + +```console +jv <file> +``` + +Run with **additional debug output**: + +```console +jv <file> -d +``` + + +With **runtime parameters**: + +```console +jv -e <param>=<value> -e <param>=<value> ... <file> +``` + +### Debug a `.jv` file + +Print debugging is further configured by the parameters `--debug-granularity` and `--debug-target`. + +```console +jv <file> -d -dg peek +``` +The value of the parameter `--debug-granularity` (short `-dg`) can have the following values: +- `peek` to log a short summary, including a small subset of data +- `exhaustive` to log a summary, including the full data +- `minimal` to log a summary, including no additional data (default). +To see logs, debugging has to be enabled using the `-d` flag. + +```console +jv <file> -d --debug-granularity peek +``` +The parameter `--debug-target` (short `-dt`) allows to specify which blocks should be logged for debugging. Separate block names by comma if multiple blocks are targeted. All blocks are logged if the parameter is omitted. +```console +jv <file> -d --debug-granularity peek --debug-target MyExtractorBlock,MySinkBlock +``` + + +## Examples + +You can find multiple examples [here](https://github.com/jvalue/jayvee/tree/main/example). Copy them to your local file system and execute them with the `jv` command on your command line (see [usage](#usage)). + + +## VSCode Plugin + +To set up Jayvee locally in VS Code, you need to install the latest Jayvee VS Code extension. +To install the most recent extension, go to our [latest release](https://github.com/jvalue/jayvee/releases/latest) +and download the `jayvee.vsix` file from the release assets. +Next, go to [this page](https://code.visualstudio.com/docs/editor/extension-marketplace#_install-from-a-vsix) and +follow the instructions for installing the downloaded extension. + +## Troubleshooting + +1. Error `structuredClone is not defined` + * Please make sure you use node version 17+. diff --git a/apps/docs/versioned_docs/version-0.0.17/user/intro.md.license b/apps/docs/versioned_docs/version-0.0.17/user/intro.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/intro.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.17/user/runtime-parameters.md b/apps/docs/versioned_docs/version-0.0.17/user/runtime-parameters.md new file mode 100644 index 00000000..4f257fac --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/runtime-parameters.md @@ -0,0 +1,27 @@ +--- +sidebar_position: 8 +--- + +# Runtime Parameters + +Property values in Jayvee can be assigned to `values` or left open for later configuration via `runtime parameters`. + +## Syntax + +Runtime parameters are indicated by the `requires` keyword, followed by the identifier of the parameter. Example + +```jayvee +block CarsLoader oftype SQLiteLoader { + table: "Cars"; + file: requires CARS_SQLITE_FILE; +} +``` + +## CLI + +The interpreter CLI has to define all existing runtime parameters for execution. +Use the CLI flag `-e` to to define them as key-value pairs of identifier and value. + +```console +jv -e <param>=<value> -e <param>=<value> ... <file> +``` diff --git a/apps/docs/versioned_docs/version-0.0.17/user/runtime-parameters.md.license b/apps/docs/versioned_docs/version-0.0.17/user/runtime-parameters.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/runtime-parameters.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.17/user/transforms.md b/apps/docs/versioned_docs/version-0.0.17/user/transforms.md new file mode 100644 index 00000000..731ec304 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/transforms.md @@ -0,0 +1,77 @@ +--- +sidebar_position: 7 +--- + +# Transforms + +Transforms are a concept in Jayvee to define the transformation of individual values. +They are similar to functions in programming languages, i.e. they perform computations on some input values and produce output values. Transform work by mapping input values to outputs using [expressions](./expressions.md). + +:::info Important + +Up to version `0.0.16`, we only supported a single input for transformers! + +::: + +:::info Important + +In its current state, Jayvee only supports a arbitrary numbers of inputs and a single output for transforms. +For the future, it is planned to support arbitrary numbers for outputs as well. + +::: + + +## Syntax + +The general syntax of transforms looks like this: + +```jayvee +transform <name> { + from <inputName> oftype <inputValuetype>; + to <outputName> oftype <outputValuetype>; + + <outputName>: <expression>; +} +``` + +The `transform` keyword is used to define a transform and give it a name. +The curly braces denote the body of the transform. + +The body first contains the definitions of input and output ports. +Input ports are defined using the `from` keyword whereas output ports use the `to` keyword. +Next, they are given a name and, after the `oftype` keyword, typed with a valuetype. + +Below, there needs to be an output assignment for each output port. +The output assignment defines how a particular output value is computed. +They consist of the name of an output port, followed by a `:`. +Next, an [expression](./expressions.md) specifies how the output value shall be computed. +Names of input ports can be used in such an expression to refer to input values. + +### Example + +The following transform converts temperature values from degree Celsius to Kelvin: + +```jayvee +transform CelsiusToKelvin { + from tempCelsius oftype decimal; + to tempKelvin oftype decimal; + + tempKelvin: tempCelsius + 273.15; +} +``` + +The following transform converts a text based status into a boolean value, `true` if the text is `Active`, `false` for any other value: + +```jayvee +transform StatusToBoolean { + from statusText oftype text; + to statusBoolean oftype boolean; + + statusBoolean: statusText == "Active"; +} +``` + +## Applying transforms to table columns + +Transforms can be applied to columns of a table. +Please refer to the documentation of the [`TableTransformer` block type](./block-types/TableTransformer.md) to find out how. diff --git a/apps/docs/versioned_docs/version-0.0.17/user/transforms.md.license b/apps/docs/versioned_docs/version-0.0.17/user/transforms.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/transforms.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.17/user/valuetypes/_category_.json b/apps/docs/versioned_docs/version-0.0.17/user/valuetypes/_category_.json new file mode 100644 index 00000000..d69f22af --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/valuetypes/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Valuetypes", + "position": 4, + "link": { + "type": "generated-index", + "description": "Jayvee supports these different kinds of valuetypes." + } +} diff --git a/apps/docs/versioned_docs/version-0.0.17/user/valuetypes/_category_.json.license b/apps/docs/versioned_docs/version-0.0.17/user/valuetypes/_category_.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/valuetypes/_category_.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.17/user/valuetypes/builtin-valuetypes.md b/apps/docs/versioned_docs/version-0.0.17/user/valuetypes/builtin-valuetypes.md new file mode 100644 index 00000000..0bc36dcb --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/valuetypes/builtin-valuetypes.md @@ -0,0 +1,98 @@ +--- +title: Built-in Valuetypes +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +# Description + +For an introduction to valuetypes, see the [Core Concepts](../core-concepts). +Built-in valuetypes come with the basic version of Jayvee. +They are the basis for more restricted [Primitive Valuetypes](./primitive-valuetypes) +that fullfil [Constraints](./primitive-valuetypes#constraints). + +# Available built-in valuetypes + +## Boolean + +### Description + +A boolean value. +Examples: true, false + +### Example 1 + +```jayvee +block ExampleTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "columnName" oftype boolean + ]; +} +``` + +A block of type `TableInterpreter` that + interprets data in the column `columnName` as `boolean`. + +## Decimal + +### Description + +A decimal value. +Example: 3.14 + +### Example 1 + +```jayvee +block ExampleTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "columnName" oftype decimal + ]; +} +``` + +A block of type `TableInterpreter` that + interprets data in the column `columnName` as `decimal`. + +## Integer + +### Description + +An integer value. +Example: 3 + +### Example 1 + +```jayvee +block ExampleTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "columnName" oftype integer + ]; +} +``` + +A block of type `TableInterpreter` that + interprets data in the column `columnName` as `integer`. + +## Text + +### Description + +A text value. +Example: "Hello World" + +### Example 1 + +```jayvee +block ExampleTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "columnName" oftype text + ]; +} +``` + +A block of type `TableInterpreter` that + interprets data in the column `columnName` as `text`. diff --git a/apps/docs/versioned_docs/version-0.0.17/user/valuetypes/builtin-valuetypes.md.license b/apps/docs/versioned_docs/version-0.0.17/user/valuetypes/builtin-valuetypes.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/valuetypes/builtin-valuetypes.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.17/user/valuetypes/primitive-valuetypes.md b/apps/docs/versioned_docs/version-0.0.17/user/valuetypes/primitive-valuetypes.md new file mode 100644 index 00000000..92400a53 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/valuetypes/primitive-valuetypes.md @@ -0,0 +1,48 @@ +--- +sidebar_position: 2 +--- +# Primitive ValueTypes + +`Primitive ValueTypes` are based on `Built-in ValueTypes` and use a collection of constraints to restrict the range of valid values. +Such constraints are implicitly connected via a logical `AND` relation. +Note that the `Constraints` need to be applicable to the base-type of the `ValueType` - indicated by the identifier after the keyword `oftype`: + +```jayvee +valuetype GasFillLevel oftype integer { + constraints: [ GasFillLevelRange ]; +} +``` + + +## Constraints + +`Constraints` for `ValueTypes` declare the validity criteria that each concrete value is checked against. + +### Syntax 1: Expression syntax + +The syntax of expression-based `Constraints` uses an expression that evaluates to `true` or `false` for the given `value`. The type of the values the expression is working in is indicated ofter the keyword `on`: + +```jayvee +constraint GasFillLevelRange on decimal: + value >= 0 and value <= 100; +``` + +Refer to the [Expression documentation](../expressions.md) for further reading on expressions. + + +### Syntax 2: Block-like syntax + +The syntax of `Constraints` is similar to the syntax of `Blocks`. +The availability of property keys and their respective `ValueTypes` is determined by the type of the `Constraint` - indicated by the identifier after the keyword `oftype`: + +```jayvee +constraint GasFillLevelRange oftype RangeConstraint { + lowerBound: 0; + lowerBoundInclusive: true; + upperBound: 100; + upperBoundInclusive: true; +} +``` + +Note that the type of `Constraint` also determines its applicability to `ValueTypes`. +For instance, a `RangeConstraint` can only be applied to the numerical types `integer` and `decimal`. \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.17/user/valuetypes/primitive-valuetypes.md.license b/apps/docs/versioned_docs/version-0.0.17/user/valuetypes/primitive-valuetypes.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.17/user/valuetypes/primitive-valuetypes.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.18/dev/01-intro.md b/apps/docs/versioned_docs/version-0.0.18/dev/01-intro.md new file mode 100644 index 00000000..fef3c08e --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/dev/01-intro.md @@ -0,0 +1,52 @@ +--- +sidebar_position: 1 +--- + +# Introduction for Jayvee Developers + +## How to contribute + +In order to contribute to the Jayvee project, please have a look at our [contribution guide](https://github.com/jvalue/jayvee/blob/main/CONTRIBUTING.md). Before planning a contribution, please read the [design principles](./05-design-principles.md) and consider if your changes fit the vision expressed there. + +The overall project is licensed under the `AGPL-3.0-only` license and is compliant to the [REUSE Specification](https://reuse.software/spec/) by the [Free Software Foundation Europe](https://fsfe.org/). +Because of this, contributions are required to adhere to the license and follow that specification. +More details on this topic can be found [here](./02-dev-processes/03-licensing-and-copyright.md). + +And last but not least, please read and follow our [code of conduct](https://github.com/jvalue/jayvee/blob/main/CODE_OF_CONDUCT.md). + +## Project overview + +The Jayvee repository is located [here](https://github.com/jvalue/jayvee) on GitHub. +It uses an [Nx mono-repository](https://nx.dev/) setup and contains all related projects, most notably the Jayvee language server and interpreter. +Please have a look at the [architecture overview](./03-architecture-overview.md) for more details. + +## Prerequisites + +Node.js and npm are required for development. +It is recommended to use their LTS version to avoid any potential compatibility issues. +Also, refer to this [quick start guide](https://github.com/jvalue/jayvee#development-quickstart) for developers. + +In order to run the Jayvee VS Code extension during development, VS Code is required as IDE. +Using VS Code also allows installing the [Langium VS Code extension](https://marketplace.visualstudio.com/items?itemName=langium.langium-vscode) for better IDE support when working with Langium grammar files. + +## Resources for getting started + +### Langium + +- [**Langium documentation**](https://langium.org/docs/) +- Practical examples using Langium: + - [Langium examples](https://github.com/langium/langium/tree/main/examples): Official example languages by Langium + - [Langium's grammar language](https://github.com/langium/langium/tree/main/packages/langium): The implementation of Langium's own grammar language + - [Language server for SQL](https://github.com/langium/langium-sql): An implementation of a language server for SQL + - [MiniLogo DSL](https://github.com/langium/langium-minilogo): A domain-specific language for drawing pictures + - [Lox language](https://github.com/langium/langium-lox): An implementation of the Lox scripting language (also see the "Crafting Interpreters" book below) + - [SimpleUI DSL](https://github.com/TypeFox/langium-ui-framework): A domain-specific language for generating user interfaces + - [STPA-DSL](https://github.com/kieler/stpa): A domain-specific language for System-Theoretic Process Analysis +- [Langium playground](https://langium.org/playground/): A tool for testing Langium grammars and visualizing resulting ASTs +- [Typefox blog](https://www.typefox.io/blog/): A blog by the company behind Langium, mostly posting Langium-related content. + +### Building compilers / interpreters / DSLs + +- [Crafting Interpreters](https://craftinginterpreters.com/contents.html): A book by Robert Nystrom on implementing an interpreter for the Lox scripting language +- [DSL Engineering](https://voelter.de/dslbook/markusvoelter-dslengineering-1.0.pdf): A book by Markus Voelter on designing, implementing and using domain-specific languages +- [Awesome Compilers](https://github.com/aalhour/awesome-compilers#readme): A list featuring many resources on compilers and interpreters diff --git a/apps/docs/versioned_docs/version-0.0.18/dev/01-intro.md.license b/apps/docs/versioned_docs/version-0.0.18/dev/01-intro.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/dev/01-intro.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.18/dev/02-dev-processes/01-rfc-process.md b/apps/docs/versioned_docs/version-0.0.18/dev/02-dev-processes/01-rfc-process.md new file mode 100644 index 00000000..07444085 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/dev/02-dev-processes/01-rfc-process.md @@ -0,0 +1,13 @@ +--- +title: Language Design Process (RFCs) +sidebar_position: 1 +--- + +We use RFCs to discuss changes to the language before implementing them. You can have a look at all closed (accepted / rejected) RFCs [here](https://github.com/jvalue/jayvee/pulls?q=is%3Apr+is%3Aclosed+RFC+), and all RFCs under discussion [here](https://github.com/jvalue/jayvee/pulls?q=is%3Apr+is%3Aopen+RFC). + +If you want to contribute an RFC please follow these steps: +1. Make a copy of the **template** at `rfc/0000-rfc-template.md` and place it into the `rfc` folder. +2. Create a draft for the RFC on a new branch. Follow the `TODOs` in template to do so. +3. Open a pull request with prefix `RFC <number>` in the title. +4. Address the reviews. Consider opening a new PR if major things need to be addressed and the discussion log becomes too confusing. +5. Once accepted, create an issue with the necessary steps to implement the RFC. \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.18/dev/02-dev-processes/01-rfc-process.md.license b/apps/docs/versioned_docs/version-0.0.18/dev/02-dev-processes/01-rfc-process.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/dev/02-dev-processes/01-rfc-process.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.18/dev/02-dev-processes/02-debug-vs-code-extension.md b/apps/docs/versioned_docs/version-0.0.18/dev/02-dev-processes/02-debug-vs-code-extension.md new file mode 100644 index 00000000..5fbcf07c --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/dev/02-dev-processes/02-debug-vs-code-extension.md @@ -0,0 +1,25 @@ +--- +title: Debugging via the VS Code extension +sidebar_position: 2 +--- + +The VS Code extension of Jayvee can be used to interactively debug the language server. +During development, when using VS Code as IDE, another instance of VS Code can be opened which has the most recent, locally built VS Code extension loaded. +This can be achieved using the launch configuration `Run extension` the `Run and Debug` side-menu of VS Code or by pressing the `F5` key if that launch configuration is already selected there. + +Note that there is no file watching mechanism involved. +So in order to reflect changes to the source code, the additional VS Code instance has to be **closed and reopened** as described above. + +## How to attach a debugger + +1. Use the launch configuration `Run extension` to open the additional VS Code instance with the extension loaded +2. Use the launch configuration `Attach to Language Server` to attach the debugger + +Any set breakpoints should now be marked as active and pause the execution once they are reached. + +## How to view logs of the language server + +In the additional VS Code instance, it is possible to view the logs of the language server. +They might also be helpful for debugging since any uncaught errors are logged with their stack trace by the Langium framework. + +To view the logs, open the bottom panel called `Output` and select `Jayvee` in the dropdown menu. diff --git a/apps/docs/versioned_docs/version-0.0.18/dev/02-dev-processes/02-debug-vs-code-extension.md.license b/apps/docs/versioned_docs/version-0.0.18/dev/02-dev-processes/02-debug-vs-code-extension.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/dev/02-dev-processes/02-debug-vs-code-extension.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.18/dev/02-dev-processes/03-licensing-and-copyright.md b/apps/docs/versioned_docs/version-0.0.18/dev/02-dev-processes/03-licensing-and-copyright.md new file mode 100644 index 00000000..201e21ca --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/dev/02-dev-processes/03-licensing-and-copyright.md @@ -0,0 +1,69 @@ +--- +title: Licensing and copyright +sidebar_position: 3 +--- + +The [Jayvee repository](https://github.com/jvalue/jayvee) is REUSE compliant, meaning that it fulfills the +[REUSE Specification](https://reuse.software/spec/) by the [Free Software Foundation Europe](https://fsfe.org/) (FSFE). +This makes clear under which license each project file is licensed under and who owns the copyright, not only for +humans but also for machines. + +This is done by explicitly adding copyright and licensing information to each file of the project. This is achieved +by either using a comment header or a separate `*.license` file in case comments are not possible. + +See <https://reuse.software/> more information. + +## What license is used in this project and who is the copyright holder? + +The entire project is licensed under [AGPL-3.0-only](https://spdx.org/licenses/AGPL-3.0-only.html), the +license file can be found [here](https://github.com/jvalue/jayvee/blob/main/LICENSES/AGPL-3.0-only.txt). +The copyright holder is the [Friedrich-Alexander-Universität Erlangen-Nürnberg](https://www.fau.eu/). + +## How to submit a REUSE compliant contribution? + +In case you want to contribute to the project, you will need to ensure that all of your contributed files are REUSE +compliant. In order to achieve this, you need to add a key-value pair for both copyright and licensing information +following this schema: + +``` +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only +``` + +In case a file allows comments, use single-line comments to add the copyright and licensing information at the top +of the file. Otherwise, create a corresponding `*.license` file with the text above as its content. You can have a +look at some existing project files to get an impression on it is done in practice. + +For files with common file extensions, you can use the [reuse CLI tool](https://github.com/fsfe/reuse-tool) to add +licensing and copyright information automatically. + +For more details, you can have a look at the [Getting Started tutorial](https://reuse.software/tutorial/) on the REUSE +website. + +## How to validate REUSE compliance? + +When you make a contribution and open a new pull request, the CI checks whether your contribution is REUSE compliant +using the [reuse CLI tool](https://github.com/fsfe/reuse-tool). + +In order to validate REUSE compliance in your local development environment, you have to install the +[reuse CLI tool](https://github.com/fsfe/reuse-tool) and run the following command in the projects' root folder: + +```bash +reuse lint +``` + +You can also set up a pre-commit hook, so the above command is run before each commit. +See [here](https://reuse.readthedocs.io/en/latest/readme.html#run-as-pre-commit-hook) for details on how to set it up. + +## How to hide `*.license` files in IDEs + +During development, the file explorer of your IDE may be cluttered due to the numerous `*.license` files in the +project. Luckily, most IDEs allow hiding certain files, e.g. by specifying a pattern to exclude them from the +explorer. + +Below, you can find instructions on how to hide `*.license` files in commonly used IDEs: +- **Visual Studio Code**: Add `**/*.license` to the +[`files.exclude` setting](https://code.visualstudio.com/docs/getstarted/userinterface#_explorer) +- **WebStorm**: Add `*.license` to the +[excluded files setting](https://www.jetbrains.com/help/webstorm/configuring-project-structure.html#exclude-by-pattern) diff --git a/apps/docs/versioned_docs/version-0.0.18/dev/02-dev-processes/03-licensing-and-copyright.md.license b/apps/docs/versioned_docs/version-0.0.18/dev/02-dev-processes/03-licensing-and-copyright.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/dev/02-dev-processes/03-licensing-and-copyright.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.18/dev/02-dev-processes/04-release-jayvee-version.md b/apps/docs/versioned_docs/version-0.0.18/dev/02-dev-processes/04-release-jayvee-version.md new file mode 100644 index 00000000..51630050 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/dev/02-dev-processes/04-release-jayvee-version.md @@ -0,0 +1,21 @@ +--- +title: Releasing a new Jayvee version +sidebar_position: 4 +--- + +## Version Numbers + +In this early stage of the project we do not yet follow [semantic versioning](https://semver.org/) since we expect the introduction of breaking changes frequently. +To indicate that, we only release alpha versions where the `version` is incremented with every release. +- For the npm packages, we use the version `0.0.<version>`. +- For the GitHub releases, we use the git tag `v0.0.<version>-alpha`. + +## Jayvee Release Procedure + +For releasing a new version of Jayvee, you need to complete the following steps: + +1. Increment the version in the `package.json` file. +2. Run `npx nx run docs:version-snapshot`. +3. If you are on a feature or dev branch, merge into main. +4. Create a GitHub release on the main branch. Attach a changelog. +5. The CI/CD will deal with the rest. diff --git a/apps/docs/versioned_docs/version-0.0.18/dev/02-dev-processes/04-release-jayvee-version.md.license b/apps/docs/versioned_docs/version-0.0.18/dev/02-dev-processes/04-release-jayvee-version.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/dev/02-dev-processes/04-release-jayvee-version.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.18/dev/02-dev-processes/_category_.json b/apps/docs/versioned_docs/version-0.0.18/dev/02-dev-processes/_category_.json new file mode 100644 index 00000000..c8f51245 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/dev/02-dev-processes/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Development Process", + "position": 2, + "link": { + "type": "generated-index", + "description": "Here you can find general processes around developing Jayvee." + } +} diff --git a/apps/docs/versioned_docs/version-0.0.18/dev/02-dev-processes/_category_.json.license b/apps/docs/versioned_docs/version-0.0.18/dev/02-dev-processes/_category_.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/dev/02-dev-processes/_category_.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.18/dev/03-architecture-overview.md b/apps/docs/versioned_docs/version-0.0.18/dev/03-architecture-overview.md new file mode 100644 index 00000000..cb4576fa --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/dev/03-architecture-overview.md @@ -0,0 +1,43 @@ +--- +title: Architecture overview +sidebar_position: 4 +--- + +Jayvee has a clear separation between the language itself and its execution. +This guide gives an overview of the overall architecture. + +## Language Server + +On the pure language side, the central project is the [language server](https://github.com/jvalue/jayvee/tree/main/libs/language-server) which is heavily built upon the [Langium framework](https://langium.org/). +It contains the syntax definition (i.e. the grammar) and is capable of performing static semantic analysis on models, so invalid models can be rejected and errors are reported to the user. +It uses the [Language Server Protocol](https://microsoft.github.io/language-server-protocol/) (LSP) for communicating with IDEs in order to provide common features such as diagnostics, auto completion and much more. + +## Interpreter + +The Jayvee [interpreter](https://github.com/jvalue/jayvee/tree/main/apps/interpreter) on the other hand is capable of running Jayvee models. +Therefore, it uses the language server as a library to perform lexing, parsing and semantic analysis on given models. +In case no errors were found during these phases, it executes the model by interpreting it. +This means that models are not compiled to any other language, like a compiler does, but rather directly executed on the fly without any code generation involved. +The interpreter comes with a command-line interface (CLI) for users, so they are able to execute Jayvee models easily. + +The following diagram visualizes the architecture described so far: + +```mermaid +graph LR +model[Jayvee Model] --> lexical(Lexical Analysis) +subgraph Jayvee Interpreter + subgraph Jayvee Language Server + subgraph Langium Framework + lexical --> syntax(Syntax Analysis) + end + syntax --> semantic(Semantic Analysis) + end + semantic --> interpretation(Interpretation) +end +``` + +## Jayvee Extensions + +Lastly, there are Jayvee extensions for adding additional features to Jayvee. +See [here](./04-guides/06-jayvee-extensions.md) for more details. +It is worth pointing out that extensions also follow the separation between language and execution described above. diff --git a/apps/docs/versioned_docs/version-0.0.18/dev/03-architecture-overview.md.license b/apps/docs/versioned_docs/version-0.0.18/dev/03-architecture-overview.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/dev/03-architecture-overview.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.18/dev/04-guides/01-jayvee-grammar.md b/apps/docs/versioned_docs/version-0.0.18/dev/04-guides/01-jayvee-grammar.md new file mode 100644 index 00000000..356fb09d --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/dev/04-guides/01-jayvee-grammar.md @@ -0,0 +1,34 @@ +--- +title: The Jayvee grammar +sidebar_position: 1 +--- + +The grammar of Jayvee describes the syntax and structure of the language. +It is located in the language server project. +The grammar itself is written in [Langium's own grammar language](https://langium.org/docs/grammar-language/) which is similar to [Xtext](https://www.eclipse.org/Xtext/) grammars. +Such grammar files are easily identifiable via their `.langium` file extension. + +The grammar is used to generate TypeScript interfaces that represent the Abstract Syntax Tree (AST) and different, semantically equivalent files to define syntax highlighting for different applications. +For instance, a [TextMate](https://macromates.com/manual/en/language_grammars) file is generated for the syntax highlighting in the Jayvee VS Code extension whereas a [Monarch](https://microsoft.github.io/monaco-editor/monarch.html) file is generated for the syntax highlighting in the Monaco editor. + +For further information on the grammar language of Langium, visit the corresponding [Langium documentation](https://langium.org/docs/grammar-language/). + +## Working with the grammar + +To run the code generation, either use `npm run generate` for solely the code generation or `npm run build` for an entire build. The code generation also generates further code, like the standard library. + +Whenever the grammar is changed and the code generation is run during development, it is advisory to **close and reopen the IDE**, so the changes are noticed and the file indexing is updated. + +## How to rename AST nodes + +Renaming AST nodes is not as straight forward as one might initially assume. +When renaming individual rules in the grammar, just the generated TypeScript code in `ast.ts` will reflect the renaming, but not the rest of the codebase. + +The following steps explain how to rename an AST node, so the change is reflected in the entire codebase. As an example, an AST node called `A` is supposed to be renamed to `B`: + +- Open the `ast.ts` file in the language server project +- Locate the `A` interface / type and the `isA` typeguard +- Use the rename feature by the IDE to perform the renaming from `A` to `B` and from `isA` to `isB` +- Open the grammar and locate the `A` rule +- Use the rename feature by the Langium VS Code extension to perform the renaming from `A` to `B` +- Run `npm run generate` diff --git a/apps/docs/versioned_docs/version-0.0.18/dev/04-guides/01-jayvee-grammar.md.license b/apps/docs/versioned_docs/version-0.0.18/dev/04-guides/01-jayvee-grammar.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/dev/04-guides/01-jayvee-grammar.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.18/dev/04-guides/02-working-with-the-ast.md b/apps/docs/versioned_docs/version-0.0.18/dev/04-guides/02-working-with-the-ast.md new file mode 100644 index 00000000..ca02dc33 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/dev/04-guides/02-working-with-the-ast.md @@ -0,0 +1,148 @@ +--- +title: Working with the AST +sidebar_position: 2 +--- + +The nodes of the Abstract Syntax Tree (AST) consist of types and interfaces generated from the language grammar. +See [here](./01-jayvee-grammar.md) for more information on that topic. + +The following sections provide practical guides and tips for working with nodes of the AST. + +## Dealing with potentially incomplete AST nodes + +According to the generated interfaces, properties of AST nodes are never `undefined`. +In practice however, this is not always the case. + +For example, consider the language server being confronted with an incomplete Jayvee model with syntax errors. +In such cases, properties of AST nodes are in fact `undefined`, despite their interface definition. +In the interpreter however, after lexical and syntactic analysis succeeded, it can be assumed that all AST nodes are complete (i.e. they have no undefined properties) and even that all references are resolved. + +In order to avoid accessing properties of AST nodes that are potentially undefined, it is recommended to access them via the [`?.` operator](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#optional-chaining) rather than the regular `.` operator. +Note that this might result in a conflict with the ESLint rule [`@typescript-eslint/no-unnecessary-condition`](https://typescript-eslint.io/rules/no-unnecessary-condition/), so it has to be disabled manually for such cases. + +For disabling the rule in an entire file, place this comment below the copyright and licensing header: + +```typescript +/* eslint-disable @typescript-eslint/no-unnecessary-condition */ +``` + +For just a single line, place this comment above that particular line: + +```typescript +// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition +``` + +<details> + +<summary>Full example</summary> + +Consider an exemplary AST node `A` with a property `x` of type `string`. To access that property safely: + +```typescript +import { A } from './ast' + +const astNode: A; + +// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition +const property: string | undefined = astNode?.x; +``` + +</details> + +## Usage of `assertUnreachable` + +Most times, it is beneficial to make case distinctions exhaustive, especially when working with AST nodes, properties with a [union literal type](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types) or enums. +Exhaustiveness in this context means, that the TypeScript compiler is supposed to yield an error if a case distinction does not cover all possibilities. + +Langium offers a function called `assertUnreachable` which is capable of enforcing exhaustiveness and producing compiler errors in case of violations. See the following examples to get an idea on how to use it in practice: + +<details> + +<summary>Example for an exhaustive switch statement on a union literal type</summary> + +```typescript +import { assertUnreachable } from 'langium'; + +const operator: '+' | '-'; + +switch(operator) { + case '+': { + // ... + break; + } + case '-': { + // ... + break; + } + default: { + // To ensure the switch being exhaustive on `operator`: + assertUnreachable(operator); + } +} +``` + +</details> + +<details> + +<summary>Example for an exhaustive if-elseif-else cascade using typeguards</summary> + +Consider the exemplary AST nodes `A`, `B` and `C` and that `A = B | C`: + +```typescript +import { assertUnreachable } from 'langium'; +import { A, B, isB, C, isC } from './ast' + +const astNode: A; + +if (isB(astNode)) { + // `astNode` has type `B` here +} else if (isC(astNode)) { + // `astNode` has type `C` here +} else { + // To ensure the if-elseif-else cascade being exhaustive on `astNode`: + assertUnreachable(astNode); +} +``` + +</details> + +## Usage of `assert` for expressing runtime expectations + +During development, it may occur that a certain condition is expected to **be always true** at runtime. +For example, an AST node being of a certain type or a property being defined. +The type system of TypeScript is not always able to infer such facts, so developers may have to express these expectations explicitly in their code. +Examples are [type assertions](https://www.typescriptlang.org/docs/handbook/advanced-types.html) or the [non-null assertion operator](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html#non-null-assertion-operator). +Their usage may be problematic in case the condition does not always hold, e.g. due to a bug in the program or a wrong expectation by the programmer. +In such cases, it is hard to locate the origin and debug the program because such operations are erased in the compiled JavaScript code. + +To avoid these issues, it is recommended to express such expectations as boolean expressions that are actually validated at runtime. +This can be easily achieved by using the `assert` function. +It evaluates a given boolean expression and throws an `AssertionError` in case it evaluates to `false`. +After calling `assert`, the type system of TypeScript assumes the condition to be `true` and afterwards narrows types accordingly. + +Here is an example of how to use it in practice: + +```typescript +// Import the `assert` function like this: +import { strict as assert } from 'assert'; + +import { A, B, isB } from './ast'; + +const astNode: A; +assert(isB(astNode)); +// Here `astNode` has type `B` + +const referenced = astNode?.reference?.ref; +assert(referenced !== undefined); +// Here `referred` is not `undefined` +``` + +## AST wrapper classes + +The generated interfaces for AST nodes in `ast.ts` are only meant to represent the AST structurally, they don't define any behavior. +Also, in case of syntactic sugar, there may be different kinds of AST nodes representing the same semantic language concept (e.g. single pipes with a verbose syntax or chained pipes). + +To cope with this problem, there is the concept of an `AstNodeWrapper`. +An AST node wrapper is capable of wrapping AST nodes that represent the same semantic language concept and adding behavior to them via custom methods. +To get an impression on how this can be done in practice, please have a look at the [`PipeWrapper` class](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/lib/ast/wrappers/pipe-wrapper.ts) and the [`AstNodeWrapper` interface](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/lib/ast/wrappers/ast-node-wrapper.ts). diff --git a/apps/docs/versioned_docs/version-0.0.18/dev/04-guides/02-working-with-the-ast.md.license b/apps/docs/versioned_docs/version-0.0.18/dev/04-guides/02-working-with-the-ast.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/dev/04-guides/02-working-with-the-ast.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.18/dev/04-guides/03-validation-and-diagnostics.md b/apps/docs/versioned_docs/version-0.0.18/dev/04-guides/03-validation-and-diagnostics.md new file mode 100644 index 00000000..328d5728 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/dev/04-guides/03-validation-and-diagnostics.md @@ -0,0 +1,64 @@ +--- +title: Validation and diagnostics +sidebar_position: 3 +--- + +Validation in the context of Jayvee can be understood as semantic analysis of Jayvee models. +The purpose is to uncover errors in models besides lexing, parsing and linking errors that the Langium framework already detects out of the box. +In case such errors are found, the language server makes the IDE display a diagnostic. +This means that the location of the error is underlined and an error message is shown. + +For example, each pipeline is expected to contain at least one starting block that requires no input. +Therefore, the language server analyzes every pipeline in a Jayvee model and checks for the presence of such a starting block. +In case no such block is found, the IDE underlines the pipeline definition in a red color and displays an error message. + +## How to implement validation + +### For arbitrary AST nodes + +In the file [`validation-registry.ts`](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/lib/validation/validation-registry.ts) of the language server, there is a registry containing validation functions for various AST nodes. +A validation function provided for a certain kind of AST node is called for every occurrence of such a node in a Jayvee model. +E.g. when registering a validation function for `PipelineDefinition` nodes, that function gets called once for each pipeline. + +A validation function always operates on a single, concrete AST node. +Besides the AST node, a `ValidationContext` object is passed to the function for reporting diagnostics via its `accept` method. + +The overall validation for a specific kind of AST node is usually subdivided into smaller checks that are called sequentially. +This potentially allows aborting the validation early, i.e. before all checks were run. +Aborting early may be useful if errors were reported during previous checks. +This can be determined via the `ValidationContext` which keeps track whether any errors occurred so far. + +A practical example where this plays a role are blocks: +When the type of a block is unknown to the language server, it makes no sense to validate inputs / outputs and properties of that block. +So, in case an error was reported during the check of the block type, the validation of that block is discontinued at that point. + +### For properties of blocks and constraints + +Validating property values of blocks / constraints works a bit differently because their individual validation is already incorporated into the overall validation framework. + +In general, the validation logic for a property values is located in the meta information of the block type / constraint type. +There, properties are defined using the `PropertySpecification` interface. +In order to add validation logic to a certain property, a validation function needs to be added to that property. + +For more complex validations that need to take multiple property values into account, it is also possible to provide a more general validation function which operates on the entire `PropertyBody` AST node. + +See [`text-range-selector-meta-inf.ts`](https://github.com/jvalue/jayvee/blob/main/libs/extensions/std/lang/src/text-range-selector-meta-inf.ts) for an example which includes both cases described above. + +## Reporting diagnostics + +During validation, diagnostics can be reported by calling the `accept` method of the given `ValidationContext` object. + +The call requires the severity of the diagnostic (`error`, `warning`, `info` or `hint`), the error message and its location. +The location is described by a `DiagnosticInfo` object which contains the affected AST node and optionally some further restrictions. +For more details, have a look at the [`DiagnosticInfo` interface](https://github.com/langium/langium/blob/main/packages/langium/src/validation/validation-registry.ts) by Langium. + +## Common issues + +When working on validations, a diagnostic with the following message may unexpectedly occur: `An error occurred during validation: ...` + +Such a diagnostic is generated by the Langium framework if an error object is thrown within the validation. +In most cases, the reason is the access of an `undefined` AST node property or an `AssertionError`. +For more details on such errors and how to avoid them, have a look at the documentation on [how to work with the AST](./02-working-with-the-ast.md). + +One way locate the origin of such errors is to debug the Jayvee VS Code extension, see [here](../02-dev-processes/02-debug-vs-code-extension.md) for more details. +Another possibility is to debug the interpreter and set appropriate breakpoints in the language server codebase. diff --git a/apps/docs/versioned_docs/version-0.0.18/dev/04-guides/03-validation-and-diagnostics.md.license b/apps/docs/versioned_docs/version-0.0.18/dev/04-guides/03-validation-and-diagnostics.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/dev/04-guides/03-validation-and-diagnostics.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.18/dev/04-guides/04-expressions-and-operators.md b/apps/docs/versioned_docs/version-0.0.18/dev/04-guides/04-expressions-and-operators.md new file mode 100644 index 00000000..91b51bd1 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/dev/04-guides/04-expressions-and-operators.md @@ -0,0 +1,151 @@ +--- +title: Expressions and operators +sidebar_position: 4 +--- + +Jayvee supports arbitrarily nested expressions and offers a variety of different operators. +Such expressions are similar to those used in programming languages, and they are intended to be read and understood intuitively. + +The following sections explain the definition of expressions in the language grammar, their type inference mechanism, and how the evaluation of expressions works. +As a small practical example, you may also have a look at the [arithmetics example by Langium](https://github.com/langium/langium/tree/main/examples/arithmetics) which has a heavy focus on mathematical expressions and functions. + +## Expressions in the grammar + +Expressions in the Jayvee grammar are defined in [`expression.langium`](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/grammar/expression.langium) and consist of operators (unary / binary) and literals. +Unary operators only have a single operand (e.g. the `not` operator) whereas binary operators require two operands (e.g. the `*` operator). + +The grammar is written in a way that literals end up in the leaves of the resulting AST and the nodes above represent the operators. + +As an example, have a look at the following AST structure of the expression `(-3 + 5) * 7`: + +```mermaid +classDiagram + +class `:BinaryOperator` { + operator = '*' +} + +%% Uses spaces in the name so it is unique +class ` :BinaryOperator` { + operator = '+' +} + +class `:UnaryOperator` { + operator = '-' +} + +%% Uses spaces in the name so it is unique +class ` :NumericLiteral` { + value = 3 +} + +%% Uses spaces in the name so it is unique +class ` :NumericLiteral` { + value = 5 +} + +class `:NumericLiteral` { + value = 7 +} + +`:BinaryOperator` --> ` :BinaryOperator` : leftOperand +`:BinaryOperator` --> `:NumericLiteral` : rightOperand +` :BinaryOperator` --> `:UnaryOperator` : leftOperand +`:UnaryOperator` --> ` :NumericLiteral` : operand +` :BinaryOperator` --> ` :NumericLiteral` : rightOperand +``` + +The AST is constructed in a way that ensures an unambiguous evaluation order when using depth-first search. +This implies that the AST structure already reflects the precedence and associativity of the operators, and that parentheses do not need to be explicitly represented. + +For more details on how such a grammar for expressions can be realized, see the [official Langium documentation](https://langium.org/docs/grammar-language/#tree-rewriting-actions), [this blog post](https://www.typefox.io/blog/parsing-expressions-with-xtext) and the following two sections which discuss the precedence and associativity of operators. + +### Precedence of operators + +The grammar implicitly defines the precedence of operators. +The operator precedence defines the order in which operators are evaluated within an expression. +For example, multiplication has a higher precedence than addition, which leads to multiplications being performed before additions. +In order to manually override such precedence conventions, parentheses can be used in expressions. + +The diagram below shows a more extensive precedence hierarchy, focused on common arithmetic operators. +Note that the hierarchy is arranged in ascending order, according to the operator precedence. +Such an order is similar to how operators in the actual Jayvee grammar are arranged: + +```mermaid +graph TD + subgraph BinaryOperators + add/sub(Addition Operator / Subtraction operator) --> mul/div(Multiplication Operator / Division Operator) + mul/div --> exp/root(Exponential Operator / Root Operator) + end + subgraph UnaryOperators + exp/root --> plus/minus(Plus Operator / Minus Operator) + end + plus/minus --> literal(Numeric Literal) +``` + +In the Jayvee grammar, every level of precedence is represented by a single grammar rule. +Within each such rule, the grammar rule which defines the operators with the next higher precedence is referenced. +E.g. the `AdditiveExpression` rule refers to the `MultiplicativeExpression` rule because multiplicative operators are supposed to have the next higher precedence. + +In order to alter the precedence of operators, their hierarchy of grammar rules has to be adjusted accordingly in [`expression.langium`](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/grammar/expression.langium). + +### Associativity of operators + +The associativity of operators defines the order in which operators with the same precedence are evaluated when they appear in succession without parentheses. +Operators may be either **left-associative**, **right-associative** or **non-associative**. +For example, depending on the associativity of the binary `+` operator, the expression `a + b + c` has different semantics: + +- Left-associative: `(a + b) + c` (evaluation from left to right) +- Right-associative: `a + (b + c)` (evaluation from right to left) +- Non-associative: _syntax error_, the operator cannot be chained + +The associativity of operators is also defined in the Jayvee grammar, more specifically by the structure of the grammar rules that define operators. +For details on how to encode the different kinds of associativity in grammar rules, have a look at the "Associativity" section near the end of [this blog post](https://www.typefox.io/blog/parsing-expressions-with-xtext). +The patterns described in the linked article can be found in [`expression.langium`](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/grammar/expression.langium), so each operator grammar rule encodes its own associativity. + +## Typing of expressions + +Jayvee has a mechanism for inferring and validating the type of a given expression. +This is achieved using a recursive algorithm which performs depth-first search on a given expression: + +The base case for the algorithm are literals, i.e. the tree leaves of an expression. +Depending on the type of literal, their type can be inferred trivially (in case of value literals) or otherwise from the context where the expression is located. + +For operators, the types of their operands are first inferred via recursion, and then it is checked whether they are supported by the operator. +Next, given the operand types, the resulting type is computed. +Such behavior is defined in a _type computer class_ located [here](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/lib/ast/expressions/type-computers). +Additionally, in [`operator-registry.ts`](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/lib/ast/expressions/operator-registry.ts), a type computer is registered for each kind of operator. + +In case the algorithm fails to infer a type, e.g. due to unsupported operand types or unresolved references in literals, the resulting type is `undefined`. +In order to report diagnostics in such cases, a `ValidationContext` object can be supplied when calling the type inference. + +## Evaluation of expressions + +The evaluation has the purpose of computing a single value out of an expression. +For a successful evaluation, it is a **precondition** that **a type could successfully be inferred** from the respective expression. +Also, the **value for each free variable** in that expression (i.e. literals resembling placeholders for values) needs to be **known beforehand**. +Therefore, an `ExecutionContext` object is passed to the evaluation which holds values for free variables in the current context. + +### Algorithm + +The algorithm for evaluating expressions is also based on a recursive depth-first search, similar to how the type inference works: + +Again, literals are the base case. A value literal trivially evaluates to its own value. +Other literals that resemble free variables evaluate to their value provided by the given `ExecutionContext` object. + +Regarding operators, their operands first are evaluated recursively. +Next, using the concrete operand values, the operator computes its resulting value. +This is defined in operator evaluator classes located [here](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/lib/ast/expressions/evaluators). +The classes are registered in [operator-registry.ts](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/lib/ast/expressions/operator-registry.ts) for every operator, similar to the previously mentioned type computers. + +The result of an evaluation may be `undefined` in case any errors occurred. +If the preconditions were all met, such errors are most likely arithmetic errors (like division by zero). +It is possible to provide a `ValidationContext` object to the evaluation for reporting such errors as diagnostics. + +### Evaluation strategies + +There are different evaluation strategies to choose from. +They affect the way, expressions are evaluated and thus have an impact on their semantics: + +- `exhaustive`: A full depth-first search is performed, all parts of an expression are evaluated. +- `lazy`: An evaluation with the least effort is performed. Logical operators use [short circuit semantics](https://en.wikipedia.org/wiki/Short-circuit_evaluation) and binary operators don't evaluate their right operand if the left one evaluated to `undefined`. diff --git a/apps/docs/versioned_docs/version-0.0.18/dev/04-guides/04-expressions-and-operators.md.license b/apps/docs/versioned_docs/version-0.0.18/dev/04-guides/04-expressions-and-operators.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/dev/04-guides/04-expressions-and-operators.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.18/dev/04-guides/05-standard-library.md b/apps/docs/versioned_docs/version-0.0.18/dev/04-guides/05-standard-library.md new file mode 100644 index 00000000..892e7ebe --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/dev/04-guides/05-standard-library.md @@ -0,0 +1,43 @@ +--- +title: Working with the Standard Library +sidebar_position: 5 +--- + +Jayvee ships with its own standard library on board, including the most often used valuetypes, transformations, and so on. +The standard library itself is written in `.jv` files [here](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/stdlib/). + +## Standard Library Contents + +The following elements are part of the standard library: + +## Builtin Contents +The implementations of builtin contents are not expressed in Jayvee itself but on the TypeScript layer. Examples: +- **Builtin valuetypes**: These valuetypes are the base for defining user-defined valuetypes in Jayvee, e.g., `text`, `integer`, `decimal`, `boolean`. +- **Builtin iotypes**: These iotypes are used to describe in inputs and outputs of blocktypes, e.g., `Sheet`, `File`. +- **Builtin blocktypes**: These blocktypes are the very basic building blocks in Jayvee, e.g., `HttpExtractor`, `SqliteLoader`. + +Builtin definitions are usually generated and added to the standard library from the internal representations of the concepts. + +### User-defined Contents +The implementations of user-defined contents are expressed in Jayvee itself. Examples: +- **User-defined valuetypes**: These valuetypes are based on builtin or other user-defined valuetypes. Their definition is expressed natively in Jayvee, e.g., `Percent`. +- **User-defined blocktypes**: These blocktypes are based on builtin or other user-defined blocktypes. Their definition is expressed natively in Jayvee. + +We use `jv` files to add user-defined valuetypes to the standard library (see below). + + +## Extending the Standard Library + +Just add `jv` files to the directory [here](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/stdlib/). It is crawled hierarchically, meaning that you can also organize files in folders. + +## Implementation + +### 1. Code generation + +We use code generation to transform these `.jv` files into TypeScript files that the language server can used. The [generation script](https://github.com/jvalue/jayvee/tree/main/tools/scripts/language-server/generate-stdlib.mjs) is run via `npm run generate` next to the AST generation. + +### 2. Builtin libraries + +The solution we chose to implement the standard library mechanism is close to the [builtin library tutorial](https://langium.org/guides/builtin-library/) by Langium. The following components are of interest: +- [JayveeWorkspaceManager](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/lib/builtin-library/jayvee-workspace-manager.ts) in the `language-server` that registers all libraries with the langium framework. +- [StandardLibraryFileSystemProvider](https://github.com/jvalue/jayvee/tree/main/apps/vs-code-extension/src/standard-library-file-system-provider.ts) in the `vs-code-extension` that registers all libraries with the vscode plugin framework. \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.18/dev/04-guides/05-standard-library.md.license b/apps/docs/versioned_docs/version-0.0.18/dev/04-guides/05-standard-library.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/dev/04-guides/05-standard-library.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.18/dev/04-guides/06-jayvee-extensions.md b/apps/docs/versioned_docs/version-0.0.18/dev/04-guides/06-jayvee-extensions.md new file mode 100644 index 00000000..11c34e02 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/dev/04-guides/06-jayvee-extensions.md @@ -0,0 +1,280 @@ +--- +title: Jayvee Extensions +sidebar_position: 6 +--- + +## Concepts + +### Jayvee extension + +A Jayvee extension is used to add new block types to the language without having to modify the actual grammar of the language. Such a Jayvee extension usually consists of two parts: a language extension and an execution extension which are described in the upcoming two sections. + +Jayvee extensions that shall be used by default are bundled into the so-called [standard extension](https://github.com/jvalue/jayvee/tree/main/libs/extensions/std). That way, the [language server](https://github.com/jvalue/jayvee/tree/main/libs/language-server) and the [interpreter](https://github.com/jvalue/jayvee/tree/main/apps/interpreter) are able to load them out of the box. + +### Language extension + +A language extension defines meta information of block types which are required by the +[language server](https://github.com/jvalue/jayvee/tree/main/libs/language-server). +Such meta information describes properties of +block types such as their names, input / output types and their properties. + +Note that language extensions don't define any behavior. Instead, this is done by the corresponding execution extension. + +### Execution extension + +An execution extension defines behavior for block types. They build on the meta information from the corresponding +language extension, e.g. input / output types of the block need to match the signature of the execution method and +properties are accessed by their specified name. + +Execution extensions are only required by the [interpreter](https://github.com/jvalue/jayvee/tree/main/apps/interpreter) and not necessarily by the [language server](https://github.com/jvalue/jayvee/tree/main/libs/language-server) as they solely define behavior. + +## Recipes + +### Add a new Jayvee extension + +#### 1. Generate language and execution libraries + +```bash +npx nx g @nrwl/node:library --name="extensions/<extension-name>/lang" +npx nx g @nrwl/node:library --name="extensions/<extension-name>/exec" +``` + +#### 2. Create extension classes + +In `libs/extensions/<extension-name>/lang/src/extension.ts`: + +```typescript +import { + BlockMetaInformation, + ConstructorClass, + JayveeLangExtension, +} from '@jvalue/jayvee-language-server'; + +export class MyLangExtension implements JayveeLangExtension { + getBlockMetaInf(): Array<ConstructorClass<BlockMetaInformation>> { + return []; + } +} + +``` + +In `libs/extensions/<extension-name>/exec/src/extension.ts`: + +```typescript +import { BlockExecutorClass, JayveeExecExtension } from '@jvalue/jayvee-execution'; + +export class MyExecExtension implements JayveeExecExtension { + getBlockExecutors(): BlockExecutorClass[] { + return []; + } +} +``` + +#### 3. Export extension classes + +In `libs/extensions/<extension-name>/lang/src/index.ts`: + +```typescript +export * from './extension'; +``` + +In `libs/extensions/<extension-name>/exec/src/index.ts`: + +```typescript +export * from './extension'; +``` + +#### 4. Register new extension classes in the standard extension + +In `libs/extensions/std/lang/src/extension.ts`: + +```typescript +// ... + +import { MyLangExtension } from '@jvalue/jayvee-extensions/<extension-name>/lang'; + +export class StdLangExtension implements JayveeLangExtension { + private readonly wrappedExtensions: JayveeLangExtension[] = [ + // ... + // Register your language extension here: + new MyLangExtension(), + // ... + ]; + + // ... +} +``` + +In `libs/extensions/std/exec/src/extension.ts`: + +```typescript +// ... + +import { MyExecExtension } from '@jvalue/jayvee-extensions/<extension-name>/exec'; + +export class StdExecExtension implements JayveeExecExtension { + private readonly wrappedExtensions: JayveeExecExtension[] = [ + // ... + // Register your execution extension here: + new MyExecExtension(), + // ... + ]; + + // ... +} +``` + +### Add a new block type in a Jayvee extension + +#### 1. Implement `BlockMetaInformation` + +The following example defines a block type called `MyExtractor`. This block type, for instance, takes no input and +outputs a sheet. Furthermore, it defines two properties: + +- `url` of type text +- `limit` of type integer and default value `10` + - Considered optional due to the specified default value + +In `libs/extensions/<extension-name>/lang/src/lib/my-extractor-meta-inf.ts`: + +```typescript +import { + BlockMetaInformation, + IOType, + PrimitiveValuetypes, +} from '@jvalue/jayvee-language-server'; + +export class MyExtractorMetaInformation extends BlockMetaInformation { + constructor() { + super( + // How the block type should be called: + 'MyExtractor', + + // Property definitions: + { + url: { + type: PrimitiveValuetypes.Text, + }, + limit: { + type: PrimitiveValuetypes.Integer, + defaultValue: 10, + }, + }, + + // Input type: + IOType.NONE, + + // Output type: + IOType.SHEET, + ); + } +} +``` + +> **Note** +> Use `IOType.NONE` whenever you expect no input or output: +> +> - Use it as input type if you want to define an extractor +> - Use it as output type if you want to define a loader + +#### 2. Register the new `BlockMetaInformation` in the language extension + +In `libs/extensions/<extension-name>/lang/src/extension.ts`: + +```typescript +// ... + +import { MyExtractorMetaInformation } from './lib/my-extractor-meta-inf'; + +export class MyLangExtension implements JayveeLangExtension { + getBlockMetaInf(): Array<ConstructorClass<BlockMetaInformation>> { + return [ + // ... + // Register your meta information here: + MyExtractorMetaInformation, + // ... + ]; + } +} +``` + +#### 3. Implement `BlockExecutor` + +The following example implements an executor for the previously defined block type `MyExtractor`. + +The `execute` method defines the behavior when a block is executed. Its signature matches the input and output types defined in `MyExtractorMetaInformation`. + +In `libs/extensions/<extension-name>/exec/src/lib/my-extractor-executor.ts`: + +```typescript +import * as R from '@jvalue/jayvee-execution'; +import { + AbstractBlockExecutor, + BlockExecutorClass, + ExecutionContext, + Sheet, + implementsStatic, +} from '@jvalue/jayvee-execution'; +import { IOType, PrimitiveValuetypes } from '@jvalue/jayvee-language-server'; + +@implementsStatic<BlockExecutorClass>() +export class MyExtractorExecutor + extends AbstractBlockExecutor<IOType.NONE, IOType.SHEET> +{ + // Needs to match the type in meta information: + public static readonly type = 'MyExtractor'; + + public readonly inputType = IOType.NONE; + public readonly outputType = IOType.SHEET; + + async doExecute( + input: None, + context: ExecutionContext, + ): Promise<R.Result<Sheet>> { + // Accessing property values by their name: + const url = context.getPropertyValue( + 'url', + PrimitiveValuetypes.Text, + ); + const limit = context.getPropertyValue( + 'limit', + PrimitiveValuetypes.Integer, + ); + + // ... + + if (error) { + return R.err(...); + } + + return R.ok(...); + } +} +``` + +> **Info** +> The interface `BlockExecutor<I,O>` is used as an API for block executors. The abstract class `AbstractBlockExecutor<I,O>` gives some further functionality for free, e.g., debug logging. + +> **Warning** +> The generic types of `AbstractBlockExecutor<I,O>` need to match the input and output types of the corresponding `BlockMetaInformation`. + +#### 4. Register the new `BlockExecutor` in the execution extension + +In `libs/extensions/<extension-name>/exec/src/extension.ts`: + +```typescript +// ... + +import { MyExtractorExecutor } from './lib/my-extractor-executor'; + +export class MyExecExtension implements JayveeExecExtension { + getBlockExecutors(): BlockExecutorClass[] { + return [ + // ... + // Register your block executor here: + MyExtractorExecutor, + // ... + ]; + } +} +``` diff --git a/apps/docs/versioned_docs/version-0.0.18/dev/04-guides/06-jayvee-extensions.md.license b/apps/docs/versioned_docs/version-0.0.18/dev/04-guides/06-jayvee-extensions.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/dev/04-guides/06-jayvee-extensions.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.18/dev/04-guides/_category_.json b/apps/docs/versioned_docs/version-0.0.18/dev/04-guides/_category_.json new file mode 100644 index 00000000..80f6fce8 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/dev/04-guides/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Guides", + "position": 4, + "link": { + "type": "generated-index", + "description": "Here you can find guides that will help you developing certain aspects of Jayvee." + } +} diff --git a/apps/docs/versioned_docs/version-0.0.18/dev/04-guides/_category_.json.license b/apps/docs/versioned_docs/version-0.0.18/dev/04-guides/_category_.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/dev/04-guides/_category_.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.18/dev/05-design-principles.md b/apps/docs/versioned_docs/version-0.0.18/dev/05-design-principles.md new file mode 100644 index 00000000..8b15dd8c --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/dev/05-design-principles.md @@ -0,0 +1,24 @@ +--- +title: Design Principles +sidebar_position: 5 +--- + +When deciding on new features for the domain-specific language itself, we try to adhere to the following high level guidelines. Of course, none of these statements is set in stone and every decision is a tradeoff. + +## Jayvee Manifesto +_Inspired by the [Agile Manifesto](https://agilemanifesto.org/)._ + +We are uncovering better ways of _modeling data pipelines by providing a domain-specific language for data engineering and making it easy for everyone to participate in it_. + +Through this work we have come to value: + +1. **Describing a goal state** over how to get there. +2. **Explicit modeling** over hidden magic. +3. **Composition** over inheritance. +4. **Flat structures** over deep nesting. + +That is, while there is value in the items on the right, we value the items on the left more. + +Through this work, we also have come to explore: + +5. **Libraries** over language features. \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.18/dev/05-design-principles.md.license b/apps/docs/versioned_docs/version-0.0.18/dev/05-design-principles.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/dev/05-design-principles.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.18/dev/12-jayvee-testing.md b/apps/docs/versioned_docs/version-0.0.18/dev/12-jayvee-testing.md new file mode 100644 index 00000000..7936a75f --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/dev/12-jayvee-testing.md @@ -0,0 +1,249 @@ +--- +title: Writing tests for Jayvee +--- + +In order to ensure that Jayvee works as intended and to catch breaking changes, we have implemented the following components for regression testing: +- Testing utils: utils to create Langium Typescript objects from *.jv test assets (see [here](#testing-utils)) as well as mocks for execution testing (see [here](#testing-utils-1)) +- [Grammar tests](#grammar-tests): test the grammar parsing and validation +- [Execution tests](#execution-tests): test the execution of blocks + +## Conventions +All of the existing tests follow these conventions: +1. The `<file-name>.spec.ts` file is located next to the `<file-name>.ts` file itself. +2. The `*.jv` assets are located inside a `test/assets/<file-name>` folder. +Take a look at one of the exisiting tests for more details. + +## Grammar tests +These kind of tests are mainly located inside the [language-server](https://github.com/jvalue/jayvee/tree/main/libs/language-server) as well as the language parts of each extension (for example [std/lang](https://github.com/jvalue/jayvee/tree/main/libs/extensions/std/lang)). + +### Testing utils +The testing utils are located inside the `language-server` in a dedicated [test folder](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/test). +These utils can be imported using `@jvalue/jayvee-language-server/test` and contain the following parts: + +[**langium-utils.ts**](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/test/langium-utils.ts): +This utils file contains two functions: +- `parseHelper` to simplify parsing the input (content of a *.jv file) and returning the corresponding `LangiumDocument`, and +- `validationHelper` parse and validate the created document. +They are kept in a separate file due to being copied from the Langium repository and thus subject to a different code license and copyright. + +[**utils.ts**](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/test/utils.ts): +This file contains custom testing utility utils functions, like `readJvTestAssetHelper` for reading jv test assets. +Example: +``` ts +import * as path from 'path'; + +import { createJayveeServices } from '@jvalue/jayvee-language-server'; +import { + ParseHelperOptions, + expectNoParserAndLexerErrors, + parseHelper, + readJvTestAssetHelper, +} from '@jvalue/jayvee-language-server/test'; +import { AstNode, LangiumDocument } from 'langium'; + +describe('My example test', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../test/assets/', // localized path to test assets folder + ); + + beforeAll(() => { + // [...] register extensions etc + const services = createJayveeServices(NodeFileSystem).Jayvee; // Or retrieve them if services already exist + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + // [...] + + it('My dummy test', () => { + const text = readJvTestAsset('<sub-folder>/<test-asset-name>.jv'); + + const document = await parse(text); + expectNoParserAndLexerErrors(document); + // Rest of test + }); +}); +``` +If you want to simply validate the test assets, simply replace `parseHelper` with `validationHelper` (and adjust the types). +You can find detailed documentation of all the utility functions directly in the code. + +[**extension/**](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/test/extension): +This folder contains a Jayvee extension for testing. +If there are certain blocks required for testing a certain feature, they can be defined here. +One such example is the already defined `TestProperty` block which has a multitude of different properties, each with a different type. +This block is used for testing properties and property-assignments. +The extension provides loader and extractor blocks for all IOTypes without any properties. +These blocks are automatically generated at runtime with the following naming scheme: +`Test${ioType}${io === 'input' ? 'Loader' : 'Extractor'}` (Example: `TestFileExtractor`). +This allows for easy (grammar) testing of non loader/extractor blocks: +``` jv +pipeline Pipeline { + + TestExtractor -> BlockUnderTest -> TestLoader; + + block BlockUnderTest oftype CellWriter { + at: range A1:A3; + write: ['values', 'to', 'write']; + } + + block TestExtractor oftype TestSheetExtractor { } + block TestLoader oftype TestSheetLoader { } +} +``` + +### Existing tests +Currently there are already tests for the following parts: +- Language-server validation checks (located [here](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/lib/validation)) +- Language-server constraint validation (located [here](https://github.com/jvalue/jayvee/tree/dev/libs/language-server/src/lib/constraint)) +- Custom block (property) validation of the three existing extensions (std extension located [here](https://github.com/jvalue/jayvee/blob/dev/libs/extensions/std/lang/src)) +- Grammar validation tests for all official full examples from the [/example](https://github.com/jvalue/jayvee/tree/main/example) folder (located [here](https://github.com/jvalue/jayvee/blob/dev/libs/extensions/std/lang/src/example-validation.spec.ts)) +- Grammar validation tests for all block examples of the std extension (located [here](https://github.com/jvalue/jayvee/blob/dev/libs/extensions/std/lang/src/meta-inf-example-validation.spec.ts)) + +## Execution tests +These kind of tests are mainly located inside the [interpreter](https://github.com/jvalue/jayvee/tree/main/libs/language-server), the [interpreter-lib](https://github.com/jvalue/jayvee/tree/dev/libs/interpreter-lib), the [execution lib](https://github.com/jvalue/jayvee/tree/dev/libs/execution) as well as the execution parts of each extension (for example [std/exec](https://github.com/jvalue/jayvee/tree/main/libs/extensions/std/exec)). + +### Testing utils +The testing utils for execution tests are spread between the extensions, with the interfaces and base utils located inside the [execution lib](https://github.com/jvalue/jayvee/tree/dev/libs/execution). +They can be imported using `@jvalue/jayvee-extensions/rdbms/test`, `@jvalue/jayvee-extensions/std/test` and `@jvalue/jayvee-execution/test`. + +[**utils.ts**](https://github.com/jvalue/jayvee/blob/dev/libs/execution/test/utils.ts): +At the moment this only contains two functions: +- `clearBlockExecutorRegistry` for clearing the registry containing all `BlockExecutor`s, and +- `clearConstraintExecutorRegistry` clearing the corresponding `ConstraintExecutor`s registry. +They are required in case the tested method initializes Jayvee itself (see [smoke test](#existing-tests-1)). + +[**block-executor-mocks.ts**](https://github.com/jvalue/jayvee/blob/dev/libs/execution/test/block-executor-mock.ts): +`BlockExecutorMock` interface for defining mocks for `AbstractBlockExecutor`. Generally only loader and executor blocks require mocks, because they interact with "the outside world" (i.e. `HttpExtractor` making http calls). +Due to how vastly different each `BlockExecutor` can be, this interface is very simple, containing only a `setup(...args: unknown[])` and a `restore()` method. See below for existing implementations. + +[**rdbms/exec/test**](https://github.com/jvalue/jayvee/tree/dev/libs/extensions/rdbms/exec/test): +Contains the implementation of `BlockExecutorMock` for `PostgresLoaderExecutor` and `SQLiteLoaderExecutor`. +Both of these executors are mocked using `jest.mock` to mock the corresponding libraries (`pg` and `sqlite3`) +**Usage:** +``` ts +import { + PostgresLoaderExecutorMock, + SQLiteLoaderExecutorMock, +} from '@jvalue/jayvee-extensions/rdbms/test'; + +// Global mocking of external library at the top of test file required, +// even though the mocking is encapsulated in helper classes +jest.mock('pg', () => { + const mClient = { + connect: jest.fn(), + query: jest.fn(), + end: jest.fn(), + }; + return { Client: jest.fn(() => mClient) }; +}); +jest.mock('sqlite3', () => { + const mockDB = { + close: jest.fn(), + run: jest.fn(), + }; + return { Database: jest.fn(() => mockDB) }; +}); + +describe('Dummy describe', () => { + // [...] + + let postgresLoaderMock: PostgresLoaderExecutorMock; + let sqliteLoaderMock: SQLiteLoaderExecutorMock; + + beforeAll(() => { + postgresLoaderMock = new PostgresLoaderExecutorMock(); + sqliteLoaderMock = new SQLiteLoaderExecutorMock(); + }); + + afterEach(() => { + postgresLoaderMock.restore(); + sqliteLoaderMock.restore(); + }); + + it('Dummy test', async () => { + // Prepare mocks + postgresLoaderMock.setup(); + sqliteLoaderMock.setup(); + + // [...] execute test + + expect(postgresLoaderMock.pgClient.connect).toBeCalledTimes(1); + expect(postgresLoaderMock.pgClient.query).toBeCalledTimes(1); + expect(postgresLoaderMock.pgClient.end).toBeCalledTimes(1); + expect(sqliteLoaderMock.sqliteClient.run).toBeCalledTimes(1); + expect(sqliteLoaderMock.sqliteClient.close).toBeCalledTimes(1); + }); +}); +``` + +[**std/exec/test/mocks**](https://github.com/jvalue/jayvee/tree/dev/libs/extensions/std/exec/test): +Contains the implementation of `BlockExecutorMock` for `HttpExtractorExecutorMock`. +This implementation uses [nock](https://www.npmjs.com/package/nock) for mocking HTTP(S) responses. +The `setup` method is further specified requiring one parameter `registerMocks: () => Array<nock.Scope>`, which returns all used `nock.Scope` (i.e. the return value of `nock('<URL>')`), see usage below: +**Usage:** +``` ts +import * as path from 'path'; + +import { HttpExtractorExecutorMock } from '@jvalue/jayvee-extensions/std/test'; + +describe('Dummy describe', () => { + // [...] + + let httpExtractorMock: HttpExtractorExecutorMock; + + beforeAll(() => { + httpExtractorMock = new HttpExtractorExecutorMock(); + }); + + afterEach(() => { + httpExtractorMock.restore(); + }); + + it('should have no errors when executing gtfs-static-and-rt.jv example', async () => { + // Prepare mocks + httpExtractorMock.setup(() => { + return [ + nock( + '<URL_1>', + ) + .get('<PATH>') + .replyWithFile( + 200, + path.resolve(__dirname, '../test/assets/file1.zip'), + { + 'Content-Type': 'application/octet-stream', + }, + ), + nock('<URL_2>') + .get('<PATH_1>') + .replyWithFile( + 200, + path.resolve( + __dirname, + '../test/assets/file2', + ), + { + 'Content-Type': 'application/octet-stream', + }, + ) + .get('<PATH_2>') + .reply(200, { content: "My dummy json reply." }), + ]; + }) + + // [...] execute test + + expect(httpExtractorMock.nockScopes.every((scope) => scope.isDone())); + }); +}); +``` + +### Existing tests +Currently there are already tests for the following parts: +- Smoke test for official examples (located [here](https://github.com/jvalue/jayvee/blob/dev/apps/interpreter/src/examples-smoke-test.spec.ts)) diff --git a/apps/docs/versioned_docs/version-0.0.18/dev/12-jayvee-testing.md.license b/apps/docs/versioned_docs/version-0.0.18/dev/12-jayvee-testing.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/dev/12-jayvee-testing.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.18/user/block-types/ArchiveInterpreter.md b/apps/docs/versioned_docs/version-0.0.18/user/block-types/ArchiveInterpreter.md new file mode 100644 index 00000000..66929ee2 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/block-types/ArchiveInterpreter.md @@ -0,0 +1,33 @@ +--- +title: ArchiveInterpreter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `File` + +Output type: `FileSystem` + +## Description + +Interprets a `File` as an archive file and converts it to a `FileSystem`. The archive file root is considered the root of the `FileSystem`. + +## Example 1 + +```jayvee +block ZipArchiveInterpreter oftype ArchiveInterpreter { + archiveType: "zip"; +} +``` + +Interprets a `File` as a ZIP-archive and creates a `FileSystem` of its extracted contents. + +## Properties + +### `archiveType` + +Type `text` + +#### Description + +The archive type to be interpreted, e.g., "zip" or "gz". diff --git a/apps/docs/versioned_docs/version-0.0.18/user/block-types/ArchiveInterpreter.md.license b/apps/docs/versioned_docs/version-0.0.18/user/block-types/ArchiveInterpreter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/block-types/ArchiveInterpreter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.18/user/block-types/CSVInterpreter.md b/apps/docs/versioned_docs/version-0.0.18/user/block-types/CSVInterpreter.md new file mode 100644 index 00000000..f4a09823 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/block-types/CSVInterpreter.md @@ -0,0 +1,63 @@ +--- +title: CSVInterpreter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `TextFile` + +Output type: `Sheet` + +## Description + +Interprets an input file as a csv-file containing string-values delimited by `delimiter` and outputs a `Sheet`. + +## Example 1 + +```jayvee +block AgencyCSVInterpreter oftype CSVInterpreter { + delimiter: ";"; + } +``` + +Interprets an input file as a csv-file containing string-values delimited by `;` and outputs `Sheet`. + +## Properties + +### `delimiter` + +Type `text` + +Default: `","` + +#### Description + +The delimiter for values in the CSV file. + +#### Example 1 + +```jayvee +delimiter: "," +``` + +Commas are used to separate values in the CSV file. + +### `enclosing` + +Type `text` + +Default: `""` + +#### Description + +The enclosing character that may be used for values in the CSV file. + +### `enclosingEscape` + +Type `text` + +Default: `""` + +#### Description + +The character to escape enclosing characters in values. diff --git a/apps/docs/versioned_docs/version-0.0.18/user/block-types/CSVInterpreter.md.license b/apps/docs/versioned_docs/version-0.0.18/user/block-types/CSVInterpreter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/block-types/CSVInterpreter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.18/user/block-types/CellRangeSelector.md b/apps/docs/versioned_docs/version-0.0.18/user/block-types/CellRangeSelector.md new file mode 100644 index 00000000..075fe4a9 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/block-types/CellRangeSelector.md @@ -0,0 +1,41 @@ +--- +title: CellRangeSelector +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Sheet` + +Output type: `Sheet` + +## Description + +Selects a subset of a `Sheet` to produce a new `Sheet`. + +## Example 1 + +```jayvee +block CarsCoreDataSelector oftype CellRangeSelector { + select: range A1:E*; +} +``` + +Selects the cells in the given range and produces a new `Sheet` containing only the selected cells. + +## Properties + +### `select` + +Type `CellRange` + +#### Description + +The cell range to select. + +#### Example 1 + +```jayvee +select: range A1:E* +``` + +Select cells from `A1` to the last cell of column `E`. diff --git a/apps/docs/versioned_docs/version-0.0.18/user/block-types/CellRangeSelector.md.license b/apps/docs/versioned_docs/version-0.0.18/user/block-types/CellRangeSelector.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/block-types/CellRangeSelector.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.18/user/block-types/CellWriter.md b/apps/docs/versioned_docs/version-0.0.18/user/block-types/CellWriter.md new file mode 100644 index 00000000..fd1f5d18 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/block-types/CellWriter.md @@ -0,0 +1,89 @@ +--- +title: CellWriter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Sheet` + +Output type: `Sheet` + +## Description + +Writes textual values into cells of a `Sheet`. The number of text values needs to match the number of cells to write into. + +## Example 1 + +```jayvee +block NameHeaderWriter oftype CellWriter { + at: cell A1; + write: ["Name"]; +} +``` + +Write the value "Name" into cell `A1`. + +## Example 2 + +```jayvee +block HeaderSequenceWriter oftype CellWriter { + at: range A1:A2; + write: ["Name", "Age"]; +} +``` + +Write the values "Name", "Age" into cells `A1` and `A2`. + +## Properties + +### `write` + +Type `Collection<text>` + +#### Description + +The values to write. + +#### Example 1 + +```jayvee +write: ["Name"] +``` + +Write the value "Name" into the cell. + +#### Example 2 + +```jayvee +write: ["Name1", "Name2"] +``` + +Write the value "Name1" into the first cell and "Name2 into the second. + +### `at` + +Type `CellRange` + +#### Description + +The cells to write into. + +#### Validation + +Needs to be a one-dimensional range of cells. + +#### Example 1 + +```jayvee +at: cell A1 +``` + +Write into cell A1. + +#### Example 2 + +```jayvee +at: range A1:A3 +``` + +Write into cells A1, A2 and A3. diff --git a/apps/docs/versioned_docs/version-0.0.18/user/block-types/CellWriter.md.license b/apps/docs/versioned_docs/version-0.0.18/user/block-types/CellWriter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/block-types/CellWriter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.18/user/block-types/ColumnDeleter.md b/apps/docs/versioned_docs/version-0.0.18/user/block-types/ColumnDeleter.md new file mode 100644 index 00000000..d3b9c328 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/block-types/ColumnDeleter.md @@ -0,0 +1,53 @@ +--- +title: ColumnDeleter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Sheet` + +Output type: `Sheet` + +## Description + +Deletes columns from a `Sheet`. Column IDs of subsequent columns will be shifted accordingly, so there will be no gaps. + +## Example 1 + +```jayvee +block MpgColumnDeleter oftype ColumnDeleter { + delete: [column B]; +} +``` + +Deletes column B (i.e. the second column). + +## Properties + +### `delete` + +Type `Collection<CellRange>` + +#### Description + +The columns to delete. + +#### Validation + +You need to specify at least one column. + +#### Example 1 + +```jayvee +delete: [column B] +``` + +Delete column B. + +#### Example 2 + +```jayvee +delete: [column B, column C] +``` + +Delete column B and column C. diff --git a/apps/docs/versioned_docs/version-0.0.18/user/block-types/ColumnDeleter.md.license b/apps/docs/versioned_docs/version-0.0.18/user/block-types/ColumnDeleter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/block-types/ColumnDeleter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.18/user/block-types/FilePicker.md b/apps/docs/versioned_docs/version-0.0.18/user/block-types/FilePicker.md new file mode 100644 index 00000000..8f4a389c --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/block-types/FilePicker.md @@ -0,0 +1,33 @@ +--- +title: FilePicker +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `FileSystem` + +Output type: `File` + +## Description + +Selects one `File` from a `FileSystem` based on its relative path to the root of the `FileSystem`. If no file matches the relative path, no output is created and the execution of the pipeline is aborted. + +## Example 1 + +```jayvee +block AgencyFilePicker oftype FilePicker { + path: "./agency.txt"; +} +``` + +Tries to pick the file `agency.txt` from the root of the provided `FileSystem`. If `agency.txt` exists it is passed on as `File`, if it does not exist the execution of the pipeline is aborted. + +## Properties + +### `path` + +Type `text` + +#### Description + +The path of the file to select, relative to the root of the provided `FileSystem`. diff --git a/apps/docs/versioned_docs/version-0.0.18/user/block-types/FilePicker.md.license b/apps/docs/versioned_docs/version-0.0.18/user/block-types/FilePicker.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/block-types/FilePicker.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.18/user/block-types/GtfsRTInterpreter.md b/apps/docs/versioned_docs/version-0.0.18/user/block-types/GtfsRTInterpreter.md new file mode 100644 index 00000000..f92b27ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/block-types/GtfsRTInterpreter.md @@ -0,0 +1,81 @@ +--- +title: GtfsRTInterpreter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `File` + +Output type: `Sheet` + +## Description + +Interprets an protobuf file (binary) of type `File` by decoding the file according to `gtfs-realtime.proto`. Outputs the extracted entity defined by `entity` as a `Sheet` + +## Example 1 + +```jayvee +block GtfsRTTripUpdateInterpreter oftype GtfsRTInterpreter{ + entity: "trip_update"; +} +``` + +A file is interpretet as an GTFS-RT file, which contains TripUpdate. + +## Properties + +### `entity` + +Type `text` + +#### Description + +Entity to process from GTFS-RT-feed (`trip_update`, `alert` or `vehicle`). + We currently support following Output-Sheets, each are an equivalent to the flattened Element Index defined in <https://developers.google.com/transit/gtfs-realtime/reference#element-index> (just required fields are included)): + + Entity TripUpdate: + ``` + [ + 'header.gtfs_realtime_version', + 'header.timestamp', + 'header.incrementality', + 'entity.id', + 'entity.trip_update.trip.trip_id', + 'entity.trip_update.trip.route_id', + 'entity.trip_update.stop_time_update.stop_sequence', + 'entity.trip_update.stop_time_update.stop_id', + 'entity.trip_update.stop_time_update.arrival.time', + 'entity.trip_update.stop_time_update.departure.time', + ]; + + ``` + Entity VehiclePosition: + ``` + [ + 'header.gtfs_realtime_version', + 'header.timestamp', + 'header.incrementality', + 'entity.id', + 'entity.vehicle_position.vehicle_descriptor.id', + 'entity.vehicle_position.trip.trip_id', + 'entity.vehicle_position.trip.route_id', + 'entity.vehicle_position.position.latitude', + 'entity.vehicle_position.position.longitude', + 'entity.vehicle_position.timestamp', + ]; + ``` + + Entity Alert: + ``` + [ + 'header.gtfs_realtime_version', + 'header.timestamp', + 'header.incrementality', + 'entity.id', + 'entity.alert.informed_entity.route_id', + 'entity.alert.header_text', + 'entity.alert.description_text', + ]; + ``` + + diff --git a/apps/docs/versioned_docs/version-0.0.18/user/block-types/GtfsRTInterpreter.md.license b/apps/docs/versioned_docs/version-0.0.18/user/block-types/GtfsRTInterpreter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/block-types/GtfsRTInterpreter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.18/user/block-types/HttpExtractor.md b/apps/docs/versioned_docs/version-0.0.18/user/block-types/HttpExtractor.md new file mode 100644 index 00000000..dde1b124 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/block-types/HttpExtractor.md @@ -0,0 +1,122 @@ +--- +title: HttpExtractor +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `None` + +Output type: `File` + +## Description + +Extracts a `File` from the web. + +## Example 1 + +```jayvee +block CarsFileExtractor oftype HttpExtractor { + url: "tinyurl.com/4ub9spwz"; +} +``` + +Fetches a file from the given URL. + +## Properties + +### `url` + +Type `text` + +#### Description + +The URL to the file in the web to extract. + +#### Example 1 + +```jayvee +url: "tinyurl.com/4ub9spwz" +``` + +Specifies the URL to fetch the data from. + +### `retries` + +Type `integer` + +Default: `0` + +#### Description + +Configures how many retries should be executed after a failure fetching the data. + +#### Example 1 + +```jayvee +retries: 3 +``` + +Executes up to 3 retries if the original retry fails (so in total max. 4 requests). + +### `retryBackoffMilliseconds` + +Type `integer` + +Default: `2000` + +#### Description + +Configures the wait time in milliseconds before executing a retry. + +#### Example 1 + +```jayvee +retryBackoff: 5000 +``` + +Waits 5s (5000 ms) before executing a retry. + +### `retryBackoffStrategy` + +Type `text` + +Default: `"exponential"` + +#### Description + +Configures the wait strategy before executing a retry. Can have values "exponential" or "linear". + +#### Example 1 + +```jayvee +retryBackoffStrategy: "linear" +``` + +Waits always the same amount of time before executing a retry. + +#### Example 2 + +```jayvee +retryBackoffStrategy: "exponential" +``` + +Exponentially increases the wait time before executing a retry. + +### `followRedirects` + +Type `boolean` + +Default: `true` + +#### Description + +Indicates, whether to follow redirects on get requests. If `false`, redirects are not followed. Default `true` + +#### Example 1 + +```jayvee +url: "tinyurl.com/4ub9spwz" + followRedirects: true +``` + +Specifies the URL to fetch the data from and allows redirects. diff --git a/apps/docs/versioned_docs/version-0.0.18/user/block-types/HttpExtractor.md.license b/apps/docs/versioned_docs/version-0.0.18/user/block-types/HttpExtractor.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/block-types/HttpExtractor.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.18/user/block-types/PostgresLoader.md b/apps/docs/versioned_docs/version-0.0.18/user/block-types/PostgresLoader.md new file mode 100644 index 00000000..5dec79d9 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/block-types/PostgresLoader.md @@ -0,0 +1,78 @@ +--- +title: PostgresLoader +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Table` + +Output type: `None` + +## Description + +Loads a `Table` into a PostgreSQL database sink. + +## Example 1 + +```jayvee +block CarsLoader oftype PostgresLoader { + host: "localhost"; + port: 5432; + username: "postgres"; + password: "postgres"; + database: "CarsDB"; + table: "Cars"; +} +``` + +A local Postgres instance is filled with table data about cars. + +## Properties + +### `host` + +Type `text` + +#### Description + +The hostname or IP address of the Postgres database. + +### `port` + +Type `integer` + +#### Description + +The port of the Postgres database. + +### `username` + +Type `text` + +#### Description + +The username to login to the Postgres database. + +### `password` + +Type `text` + +#### Description + +The password to login to the Postgres database. + +### `database` + +Type `text` + +#### Description + +The database to use. + +### `table` + +Type `text` + +#### Description + +The name of the table to write into. diff --git a/apps/docs/versioned_docs/version-0.0.18/user/block-types/PostgresLoader.md.license b/apps/docs/versioned_docs/version-0.0.18/user/block-types/PostgresLoader.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/block-types/PostgresLoader.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.18/user/block-types/RowDeleter.md b/apps/docs/versioned_docs/version-0.0.18/user/block-types/RowDeleter.md new file mode 100644 index 00000000..9a2ea071 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/block-types/RowDeleter.md @@ -0,0 +1,53 @@ +--- +title: RowDeleter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Sheet` + +Output type: `Sheet` + +## Description + +Deletes one or more rows from a `Sheet`. Row IDs of subsequent rows will be shifted accordingly, so there will be no gaps. + +## Example 1 + +```jayvee +block SecondRowDeleter oftype RowDeleter { + delete: [row 2]; +} +``` + +Deletes row 2 (i.e. the second row). + +## Properties + +### `delete` + +Type `Collection<CellRange>` + +#### Description + +The rows to delete. + +#### Validation + +You need to specify at least one row. + +#### Example 1 + +```jayvee +delete: [row 2] +``` + +Delete row 2. + +#### Example 2 + +```jayvee +delete: [row 2, row 3] +``` + +Delete row 2 and row 3. diff --git a/apps/docs/versioned_docs/version-0.0.18/user/block-types/RowDeleter.md.license b/apps/docs/versioned_docs/version-0.0.18/user/block-types/RowDeleter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/block-types/RowDeleter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.18/user/block-types/SQLiteLoader.md b/apps/docs/versioned_docs/version-0.0.18/user/block-types/SQLiteLoader.md new file mode 100644 index 00000000..5a6d8133 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/block-types/SQLiteLoader.md @@ -0,0 +1,52 @@ +--- +title: SQLiteLoader +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Table` + +Output type: `None` + +## Description + +Loads a `Table` into a SQLite database sink. + +## Example 1 + +```jayvee +block CarsLoader oftype SQLiteLoader { + table: "cars"; + file: "./cars.db"; +} +``` + +A SQLite file `cars.db` is created in the working directory. Incoming data is written to the table `cars`. + +## Properties + +### `table` + +Type `text` + +#### Description + +The name of the table to write into. + +### `file` + +Type `text` + +#### Description + +The path to a SQLite file that will be created if it does not exist. Usual file extensions are `.sqlite` and `.db`. + +### `dropTable` + +Type `boolean` + +Default: `true` + +#### Description + +Indicates, whether to drop the table before loading data into it. If `false`, data is appended to the table instead of dropping it. diff --git a/apps/docs/versioned_docs/version-0.0.18/user/block-types/SQLiteLoader.md.license b/apps/docs/versioned_docs/version-0.0.18/user/block-types/SQLiteLoader.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/block-types/SQLiteLoader.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.18/user/block-types/SheetPicker.md b/apps/docs/versioned_docs/version-0.0.18/user/block-types/SheetPicker.md new file mode 100644 index 00000000..8e710e24 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/block-types/SheetPicker.md @@ -0,0 +1,33 @@ +--- +title: SheetPicker +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Workbook` + +Output type: `Sheet` + +## Description + +Selects one `Sheet` from a `Workbook` based on its `sheetName`. If no sheet matches the name, no output is created and the execution of the pipeline is aborted. + +## Example 1 + +```jayvee +block AgencySheetPicker oftype SheetPicker { + sheetName: "AgencyNames"; +} +``` + +Tries to pick the sheet `AgencyNames` from the provided `Workbook`. If `AgencyNames` exists it is passed on as `Sheet`, if it does not exist the execution of the pipeline is aborted. + +## Properties + +### `sheetName` + +Type `text` + +#### Description + +The name of the sheet to select. diff --git a/apps/docs/versioned_docs/version-0.0.18/user/block-types/SheetPicker.md.license b/apps/docs/versioned_docs/version-0.0.18/user/block-types/SheetPicker.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/block-types/SheetPicker.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.18/user/block-types/TableInterpreter.md b/apps/docs/versioned_docs/version-0.0.18/user/block-types/TableInterpreter.md new file mode 100644 index 00000000..455cabdf --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/block-types/TableInterpreter.md @@ -0,0 +1,89 @@ +--- +title: TableInterpreter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Sheet` + +Output type: `Table` + +## Description + +Interprets a `Sheet` as a `Table`. In case a header row is present in the sheet, its names can be matched with the provided column names. Otherwise, the provided column names are assigned in order. + +## Example 1 + +```jayvee +block CarsTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "name" oftype text, + "mpg" oftype decimal, + "cyl" oftype integer, + ]; +} +``` + +Interprets a `Sheet` about cars with a topmost header row and interprets it as a `Table` by assigning a primitive valuetype to each column. The column names are matched to the header, so the order of the type assignments does not matter. + +## Example 2 + +```jayvee +block CarsTableInterpreter oftype TableInterpreter { + header: false; + columns: [ + "name" oftype text, + "mpg" oftype decimal, + "cyl" oftype integer, + ]; +} +``` + +Interprets a `Sheet` about cars without a topmost header row and interprets it as a `Table` by sequentially assigning a name and a primitive valuetype to each column of the sheet. Note that the order of columns matters here. The first column (column `A`) will be named "name", the second column (column `B`) will be named "mpg" etc. + +## Properties + +### `header` + +Type `boolean` + +#### Description + +Whether the first row should be interpreted as header row. + +#### Example 1 + +```jayvee +header: true +``` + +The first row is interpreted as table header. The values in the header row will become the column names of the table. + +#### Example 2 + +```jayvee +header: false +``` + +The first row is NOT interpreted as table header and columns of the sheet are directly mapped to table columns. The column names are taken form the provided names in the `columns` property. + +### `columns` + +Type `Collection<ValuetypeAssignment>` + +#### Description + +Collection of valuetype assignments. Uses column names (potentially matched with the header or by sequence depending on the `header` property) to assign a primitive valuetype to each column. + +#### Validation + +Needs to be a collection of valuetype assignments. Each column needs to have a unique name. + +#### Example 1 + +```jayvee +columns: [ "name" oftype text ] +``` + +There is one column with the header "name". All values in this colum are typed as text. diff --git a/apps/docs/versioned_docs/version-0.0.18/user/block-types/TableInterpreter.md.license b/apps/docs/versioned_docs/version-0.0.18/user/block-types/TableInterpreter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/block-types/TableInterpreter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.18/user/block-types/TableTransformer.md b/apps/docs/versioned_docs/version-0.0.18/user/block-types/TableTransformer.md new file mode 100644 index 00000000..54d02967 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/block-types/TableTransformer.md @@ -0,0 +1,79 @@ +--- +title: TableTransformer +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Table` + +Output type: `Table` + +## Description + +Applies a transform on each value of a column. The input port type of the used transform has to match the type of the input column. + +## Example 1 + +```jayvee + +transform CelsiusToFahrenheit { + from Celsius oftype decimal; + to Fahrenheit oftype decimal; + + Fahrenheit: (Celsius * 9/5) + 32; +} + +block CelsiusToFahrenheitTransformer oftype TableTransformer { + inputColumns: ['temperature']; + outputColumn: 'temperature'; + use: CelsiusToFahrenheit; +} +``` + +Given a column "temperature" with temperature values in Celsius, it overwrites the column with computed values in Fahrenheit by using the `CelsiusToFahrenheit` transform. The transform itself is defined elsewhere in the model. + +## Example 2 + +```jayvee + +transform CelsiusToFahrenheit { + from Celsius oftype decimal; + to Fahrenheit oftype decimal; + + Fahrenheit: (Celsius * 9/5) + 32; +} + +block CelsiusToFahrenheitTransformer oftype TableTransformer { + inputColumns: ['temperatureCelsius']; + outputColumn: 'temperatureFahrenheit'; + use: CelsiusToFahrenheit; +} +``` + +Given a column "temperatureCelsius" with temperature values in Celsius, it adds a new column "temperatureFahrenheit" with computed values in Fahrenheit by using the `CelsiusToFahrenheit` transform. The transform itself is defined elsewhere in the model. + +## Properties + +### `inputColumns` + +Type `Collection<text>` + +#### Description + +The names of the input columns. The columns have to be present in the table and match with the transform's input port types. + +### `outputColumn` + +Type `text` + +#### Description + +The name of the output column. Overwrites the column if it already exists, or otherwise creates a new one. + +### `use` + +Type `Transform` + +#### Description + +Reference to the transform that is applied to the column. diff --git a/apps/docs/versioned_docs/version-0.0.18/user/block-types/TableTransformer.md.license b/apps/docs/versioned_docs/version-0.0.18/user/block-types/TableTransformer.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/block-types/TableTransformer.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.18/user/block-types/TextFileInterpreter.md b/apps/docs/versioned_docs/version-0.0.18/user/block-types/TextFileInterpreter.md new file mode 100644 index 00000000..0fefdcc2 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/block-types/TextFileInterpreter.md @@ -0,0 +1,35 @@ +--- +title: TextFileInterpreter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `File` + +Output type: `TextFile` + +## Description + +Interprets a `File` as a `TextFile`. + +## Properties + +### `encoding` + +Type `text` + +Default: `"utf-8"` + +#### Description + +The encoding used for decoding the file contents. + +### `lineBreak` + +Type `Regex` + +Default: `{}` + +#### Description + +The regex for identifying line breaks. diff --git a/apps/docs/versioned_docs/version-0.0.18/user/block-types/TextFileInterpreter.md.license b/apps/docs/versioned_docs/version-0.0.18/user/block-types/TextFileInterpreter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/block-types/TextFileInterpreter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.18/user/block-types/TextLineDeleter.md b/apps/docs/versioned_docs/version-0.0.18/user/block-types/TextLineDeleter.md new file mode 100644 index 00000000..b36a46c4 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/block-types/TextLineDeleter.md @@ -0,0 +1,23 @@ +--- +title: TextLineDeleter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `TextFile` + +Output type: `TextFile` + +## Description + +Deletes individual lines from a `TextFile`. + +## Properties + +### `lines` + +Type `Collection<integer>` + +#### Description + +The line numbers to delete. diff --git a/apps/docs/versioned_docs/version-0.0.18/user/block-types/TextLineDeleter.md.license b/apps/docs/versioned_docs/version-0.0.18/user/block-types/TextLineDeleter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/block-types/TextLineDeleter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.18/user/block-types/TextRangeSelector.md b/apps/docs/versioned_docs/version-0.0.18/user/block-types/TextRangeSelector.md new file mode 100644 index 00000000..ffdbebd0 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/block-types/TextRangeSelector.md @@ -0,0 +1,27 @@ +--- +title: TextRangeSelector +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `TextFile` + +Output type: `TextFile` + +## Description + +Selects a range of lines from a `TextFile`. + +## Properties + +### `lineFrom` + +Type `integer` + +Default: `1` + +### `lineTo` + +Type `integer` + +Default: `null` diff --git a/apps/docs/versioned_docs/version-0.0.18/user/block-types/TextRangeSelector.md.license b/apps/docs/versioned_docs/version-0.0.18/user/block-types/TextRangeSelector.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/block-types/TextRangeSelector.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.18/user/block-types/XLSXInterpreter.md b/apps/docs/versioned_docs/version-0.0.18/user/block-types/XLSXInterpreter.md new file mode 100644 index 00000000..b16befde --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/block-types/XLSXInterpreter.md @@ -0,0 +1,24 @@ +--- +title: XLSXInterpreter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `File` + +Output type: `Workbook` + +## Description + +Interprets an input file as a XLSX-file and outputs a `Workbook` containing `Sheet`s. + +## Example 1 + +```jayvee +block AgencyXLSXInterpreter oftype XLSXInterpreter { + } +``` + +Interprets an input file as a XLSX-file and outputs a `Workbook` containing `Sheet`s. + +## Properties diff --git a/apps/docs/versioned_docs/version-0.0.18/user/block-types/XLSXInterpreter.md.license b/apps/docs/versioned_docs/version-0.0.18/user/block-types/XLSXInterpreter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/block-types/XLSXInterpreter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.18/user/block-types/_category_.json b/apps/docs/versioned_docs/version-0.0.18/user/block-types/_category_.json new file mode 100644 index 00000000..740dd2f2 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/block-types/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Block Types", + "position": 3, + "link": { + "type": "generated-index", + "description": "These blocks are shipped with Jayvee and are available right out of the box." + } +} diff --git a/apps/docs/versioned_docs/version-0.0.18/user/block-types/_category_.json.license b/apps/docs/versioned_docs/version-0.0.18/user/block-types/_category_.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/block-types/_category_.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.18/user/composite-blocks.md b/apps/docs/versioned_docs/version-0.0.18/user/composite-blocks.md new file mode 100644 index 00000000..f5c9bdb5 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/composite-blocks.md @@ -0,0 +1,115 @@ +--- +sidebar_position: 4 +--- + +# Composite Blocks + +Composite blocks are a way to create new blocktypes in Jayvee by combining the functionality of existing blocks and pipes. By relying on composite blocks instead of implementing more builtin blocks in a language interpreter, Jayvee supports easy extension by users. + +Composite blocks define: +- with the `property` keyword: properties with a name and [value type](./core-concepts.md#valuetypes), optionally a default value +- with the `input` keyword: one input with a name and io type (that can be None) +- with the `output` keyword: one output with a name and io type (that can be None) +- one pipeline definition, starting from the input (using its name) and ending in the output (again using its name) +- all blocks that are used in the pipeline definition (either builtin or other composite blocks) + +## Example +As an example, the common use-case of extracting a CSV file from a webserver using HTTP. With builtin blocks, a pipeline would start with a HttpExtractor source that downloads a file from the internet and outputs a binary file. This file must be interpreted as text (using a TextFileInterpreter) and finally as Sheet (using a CSVInterpreter). + +### Implementation with builtin blocks +```mermaid +flowchart LR + A[HttpExtractor] --> B(TextFileInterpreter) + B --> C(CSVInterpreter) + C --> D(TableInterpreter) + D --> E[SQLiteSink] +``` + +A pipeline with builtin blocks is very verbose: + +```jayvee +pipeline CarsPipeline { + CarsExtractor + -> CarsTextFileInterpreter + -> CarsCSVInterpreter + -> CarsTableInterpreter + -> CarsLoader; + + block CarsExtractor oftype HttpExtractor { + url: "https://example.com/cars.csv"; + } + + block CarsTextFileInterpreter oftype TextFileInterpreter { } + + block CarsCSVInterpreter oftype CSVInterpreter { + enclosing: '"'; + } + // ... further block definitions +} +``` + +### Refactoring using composite blocks + +The common use-case of downloading a CSV file using HTTP can be refactored into a composite block. Note that we define all properties of the builtin blocks that are used as properties of the new CSVExtractor blocktype (but add fallback values). If some internal configuration is always the same, we could also not expose it as a property of the new blocktype. + +```jayvee +// Define a new blocktype named CSVExtractor outside of the pipeline +composite blocktype CSVExtractor { + // Properties of the CSVExtractor, some with default values + property url oftype text; + property delimiter oftype text: ','; + property enclosing oftype text: ''; + property enclosingEscape oftype text: ''; + + // Input and outputs + input inputName oftype None; + output outputName oftype Sheet; + + // Pipeline definition from input, over blocks defined later, to output + inputName + ->FileExtractor + ->FileTextInterpreter + ->FileCSVInterpreter + ->outputName; + + // Block definitions using values from properties by name + block FileExtractor oftype HttpExtractor { url: url; } + block FileTextInterpreter oftype TextFileInterpreter {} + + block FileCSVInterpreter oftype CSVInterpreter { + delimiter: delimiter; + enclosing: enclosing; + enclosingEscape: enclosingEscape; + } +} +``` + +With the new CSVExtractor composite blocktype, the pipeline now looks like this. + +```mermaid +flowchart LR + CSVExtractor --> D(TableInterpreter) + D --> E[SQLiteSink] + + subgraph CSVExtractor + A[HttpExtractor] --> B(TextFileInterpreter) + B --> C(CSVInterpreter) +end +``` + +If the CSVExtractor is available in the scope of the `CarsPipeline` from before (e.g., by defining it above the pipeline), it can then be used to shorten the actual pipeline code. + +```jayvee +pipeline CarsPipeline { + // HttpExtractor, TextFileInterpreter and CSVInterpreter have been replaced by CSVExtractor + CSVExtractor + -> CarsTableInterpreter + -> CarsLoader; + + block CarsExtractor oftype CSVExtractor { + url: "https://example.com/cars.csv"; + } + + // ... further block definitions +} +``` \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.18/user/composite-blocks.md.license b/apps/docs/versioned_docs/version-0.0.18/user/composite-blocks.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/composite-blocks.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.18/user/constraint-types/AllowlistConstraint.md b/apps/docs/versioned_docs/version-0.0.18/user/constraint-types/AllowlistConstraint.md new file mode 100644 index 00000000..76dd9df1 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/constraint-types/AllowlistConstraint.md @@ -0,0 +1,27 @@ +--- +title: AllowlistConstraint +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Compatible ValueType: text + +## Description + +Limits the values to a defined a set of allowed values. Only values in the list are valid. + +## Example 1 + +```jayvee +constraint TimeUnitString oftype AllowlistConstraint { + allowlist: ["ms", "s", "min", "h", "d", "m", "y"]; +} +``` + +Only allows the common abbreviations for millisecond, second, minute, etc.. + +## Properties + +### `allowlist` + +Type `Collection<text>` diff --git a/apps/docs/versioned_docs/version-0.0.18/user/constraint-types/AllowlistConstraint.md.license b/apps/docs/versioned_docs/version-0.0.18/user/constraint-types/AllowlistConstraint.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/constraint-types/AllowlistConstraint.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.18/user/constraint-types/DenylistConstraint.md b/apps/docs/versioned_docs/version-0.0.18/user/constraint-types/DenylistConstraint.md new file mode 100644 index 00000000..078f57f2 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/constraint-types/DenylistConstraint.md @@ -0,0 +1,27 @@ +--- +title: DenylistConstraint +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Compatible ValueType: text + +## Description + +Defines a set of forbidden values. All values in the list are considered invalid. + +## Example 1 + +```jayvee +constraint NoPrimaryColors oftype DenylistConstraint { + denylist: ["red", "blue", "yellow"]; +} +``` + +Denies all primary colors. + +## Properties + +### `denylist` + +Type `Collection<text>` diff --git a/apps/docs/versioned_docs/version-0.0.18/user/constraint-types/DenylistConstraint.md.license b/apps/docs/versioned_docs/version-0.0.18/user/constraint-types/DenylistConstraint.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/constraint-types/DenylistConstraint.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.18/user/constraint-types/LengthConstraint.md b/apps/docs/versioned_docs/version-0.0.18/user/constraint-types/LengthConstraint.md new file mode 100644 index 00000000..13234897 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/constraint-types/LengthConstraint.md @@ -0,0 +1,36 @@ +--- +title: LengthConstraint +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Compatible ValueType: text + +## Description + +Limits the length of a string with an upper and/or lower boundary. Only values with a length within the given range are valid. + +## Example 1 + +```jayvee +constraint JavaStringLength oftype LengthConstraint { + minLength: 0; + maxLength: 2147483647; +} +``` + +A string with 0 to 2147483647 characters. + +## Properties + +### `minLength` + +Type `integer` + +Default: `0` + +### `maxLength` + +Type `integer` + +Default: `null` diff --git a/apps/docs/versioned_docs/version-0.0.18/user/constraint-types/LengthConstraint.md.license b/apps/docs/versioned_docs/version-0.0.18/user/constraint-types/LengthConstraint.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/constraint-types/LengthConstraint.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.18/user/constraint-types/RangeConstraint.md b/apps/docs/versioned_docs/version-0.0.18/user/constraint-types/RangeConstraint.md new file mode 100644 index 00000000..1539f2e7 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/constraint-types/RangeConstraint.md @@ -0,0 +1,60 @@ +--- +title: RangeConstraint +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Compatible ValueType: decimal + +## Description + +Limits the range of a number value with an upper and/or lower boundary which can be inclusive or exclusive. Only values within the given range are considered valid. + +## Example 1 + +```jayvee +constraint HundredScale oftype RangeConstraint { + lowerBound: 1; + upperBound: 100; +} +``` + +A scale between (and including) 1 and 100. + +## Example 2 + +```jayvee +constraint HundredScale oftype RangeConstraint { + lowerBound: 1; + lowerBoundInclusive: false; + upperBound: 100; +} +``` + +A scale for numbers strictly larger than 1 and less or equal to 100. + +## Properties + +### `lowerBound` + +Type `decimal` + +Default: `null` + +### `lowerBoundInclusive` + +Type `boolean` + +Default: `true` + +### `upperBound` + +Type `decimal` + +Default: `null` + +### `upperBoundInclusive` + +Type `boolean` + +Default: `true` diff --git a/apps/docs/versioned_docs/version-0.0.18/user/constraint-types/RangeConstraint.md.license b/apps/docs/versioned_docs/version-0.0.18/user/constraint-types/RangeConstraint.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/constraint-types/RangeConstraint.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.18/user/constraint-types/RegexConstraint.md b/apps/docs/versioned_docs/version-0.0.18/user/constraint-types/RegexConstraint.md new file mode 100644 index 00000000..deb94a4b --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/constraint-types/RegexConstraint.md @@ -0,0 +1,27 @@ +--- +title: RegexConstraint +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Compatible ValueType: text + +## Description + +Limits the values complying with a regex. Only values that comply with the regex are considered valid. + +## Example 1 + +```jayvee +constraint IFOPT_Format oftype RegexConstraint { + regex: /[a-z]{2}:\d+:\d+(:\d+)?(:\d+)?/; +} +``` + +Text that complies with the IFOPT (Identification of Fixed Objects in Public Transport) DIN EN 28701:2012 format. + +## Properties + +### `regex` + +Type `Regex` diff --git a/apps/docs/versioned_docs/version-0.0.18/user/constraint-types/RegexConstraint.md.license b/apps/docs/versioned_docs/version-0.0.18/user/constraint-types/RegexConstraint.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/constraint-types/RegexConstraint.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.18/user/constraint-types/_category_.json b/apps/docs/versioned_docs/version-0.0.18/user/constraint-types/_category_.json new file mode 100644 index 00000000..cd8b1300 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/constraint-types/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Constraint Types", + "position": 6, + "link": { + "type": "generated-index", + "description": "These constraints are shipped with Jayvee and are available right out of the box." + } +} diff --git a/apps/docs/versioned_docs/version-0.0.18/user/constraint-types/_category_.json.license b/apps/docs/versioned_docs/version-0.0.18/user/constraint-types/_category_.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/constraint-types/_category_.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.18/user/core-concepts.md b/apps/docs/versioned_docs/version-0.0.18/user/core-concepts.md new file mode 100644 index 00000000..eba14831 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/core-concepts.md @@ -0,0 +1,111 @@ +--- +sidebar_position: 2 +--- + +# Core Concepts + +The core concepts of Jayvee are `Pipelines`, `Blocks`, and `ValueTypes`. + +## Pipelines + +A `Pipeline` is a sequence of different computing steps, the `Blocks`. +The default output of a block becomes the default input of the next block, building a chain of computing steps. +In the scope of a `Pipeline`, you can connect these blocks via the `pipe` syntax: + +```jayvee +pipeline CarsPipeline { + // Assumption: blocks "GasReserveHttpExtractor", "GasReserveCSVInterpreter", "GasReserveTableInterpreter", and "GasReserveLoader" are defined + + GasReserveHttpExtractor + -> GasReserveTextFileInterpreter + -> GasReserveCSVInterpreter + -> GasReserveTableInterpreter + -> GasReserveLoader; +} +``` + +Alternatively, you can use a slightly longer syntax for pipes: + +```jayvee +pipeline CarsPipeline { + // Assumption: blocks "GasReserveHttpExtractor", "GasReserveCSVInterpreter", "GasReserveTableInterpreter", and "GasReserveLoader" are defined + + pipe { + from: GasReserveHttpExtractor; + to: GasReserveTextFileInterpreter; + + } + + pipe { + from: GasReserveTextFileInterpreter; + to: GasReserveCSVInterpreter; + + } + + // etc. +} +``` + +## Blocks + +A `Block` is a processing step within a `Pipeline`. +It can have a default input and a default output. +We differentiate the following types of `Blocks`: +- `ExtractorBlocks` do not have a default input but only a default output. They model a **data source**. +- `TransformatorBlocks` have a default input and a default output. They model a **transformation**. +- `LoaderBlocks` do have a default input but nor a default output. They model a **data sink**. + +The general structure of a `Pipeline` consisting of different blocks is the following: + +```mermaid +flowchart LR + A[ExtractorBlock] --> B(TransformatorBlock) + B --> C(TransformatorBlock) + C --> D(LoaderBlock) +``` + +The common syntax of blocks is at its core a key-value map to provide configuration to the block. +The availability of property keys and their respective `ValueTypes` is determined by the type of the `Block` - indicated by the identifier after the keyword `oftype`: + +```jayvee +block GasReserveHttpExtractor oftype HttpExtractor { + // key: value + url: "https://www.bundesnetzagentur.de/_tools/SVG/js2/_functions/csv_export.html?view=renderCSV&id=1089590"; +} +``` + +In the example above, the `url` property of type `text` is defined by the corresponding `HttpExtractor` block type. + +Blocks can be either defined as part of the language, called `builtin` or defined as composition of existing blocks by users in Jayvee, called `composite`. See the documentation for [Composite Blocks](./composite-blocks.md). + +## ValueTypes + +A `ValueType` is the definition of a data type of the processed data. +Some `Blocks` use `ValueTypes` to define logic (like filtering or assessing the data type in a data sink). +We differentiate the following types of `ValueTypes`: +- `Built-in ValueTypes` come with the basic version of Jayvee. See [Built-in Valuetypes](./valuetypes/builtin-valuetypes). +- `Primitive ValueTypes` can be defined by the user to model domain-specific data types and represent a single value. + `Constraints` can be added to a `Primitive ValueType`. +See [Primitive Valuetypes](./valuetypes/primitive-valuetypes). +- `Compound ValueTypes`: UPCOMING. + +```jayvee +valuetype GasFillLevel oftype integer { + constraints: [ GasFillLevelRange ]; +} + +constraint GasFillLevelRange on decimal: + value >= 0 and value <= 100; +``` + +## Transforms +`Transforms` are used to transform data from one `ValueType` to a different one. For more details, see [Transforms](./transforms.md). + +```jayvee +transform CelsiusToKelvin { + from tempCelsius oftype decimal; + to tempKelvin oftype decimal; + + tempKelvin: tempCelsius + 273.15; +} +``` \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.18/user/core-concepts.md.license b/apps/docs/versioned_docs/version-0.0.18/user/core-concepts.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/core-concepts.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.18/user/examples/_category_.json b/apps/docs/versioned_docs/version-0.0.18/user/examples/_category_.json new file mode 100644 index 00000000..65abd42e --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/examples/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Jayvee Examples", + "position": 10, + "link": { + "type": "generated-index", + "description": "Examples of Jayvee models" + } +} diff --git a/apps/docs/versioned_docs/version-0.0.18/user/examples/_category_.json.license b/apps/docs/versioned_docs/version-0.0.18/user/examples/_category_.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/examples/_category_.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.18/user/examples/cars.md b/apps/docs/versioned_docs/version-0.0.18/user/examples/cars.md new file mode 100644 index 00000000..e53ede09 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/examples/cars.md @@ -0,0 +1,115 @@ +--- +title: cars +--- + +```jayvee +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// Example 1: Cars +// Learning goals: +// - Understand the core concepts pipeline, block, and pipe +// - Understand the general structure of a pipeline + +// 1. This Jayvee model describes a pipeline +// from a CSV file in the web +// to a SQLite file sink. +pipeline CarsPipeline { + + // 2. We describe the structure of the pipeline, + // usually at the top of the pipeline. + // by connecting blocks via pipes. + + // 3. Verbose syntax of a pipe + // connecting the block CarsExtractor + // with the block CarsTextFileInterpreter. + pipe { + from: CarsExtractor; + to: CarsTextFileInterpreter; + } + + // 4. The output of the "from" block is hereby used + // as input for the "to" block. + + // 5. More convenient syntax of a pipe + CarsTextFileInterpreter -> CarsCSVInterpreter; + + // 6. Pipes can be further chained, + // leading to an overview of the pipeline. + CarsCSVInterpreter + -> NameHeaderWriter + -> CarsTableInterpreter + -> CarsLoader; + + + // 7. Below the pipes, we usually define the blocks + // that are connected by the pipes. + + // 8. Blocks instantiate a blocktype by using the oftype keyword. + // The blocktype defines the available properties that the block + // can use to specify the intended behavior of the block + block CarsExtractor oftype HttpExtractor { + + // 9. Properties are assigned to concrete values. + // Here, we specify the URL where the file shall be downloaded from. + url: "https://gist.githubusercontent.com/noamross/e5d3e859aa0c794be10b/raw/b999fb4425b54c63cab088c0ce2c0d6ce961a563/cars.csv"; + } + + // 10. The HttpExtractor requires no input and produces a binary file as output. + // This file has to be interpreted, e.g., as text file. + block CarsTextFileInterpreter oftype TextFileInterpreter { } + + // 11. Next, we interpret the text file as sheet. + // A sheet only contains text cells and is useful for manipulating the shape of data before assigning more strict value types to cells. + block CarsCSVInterpreter oftype CSVInterpreter { + enclosing: '"'; + } + + // 12. We can write into cells of a sheet using the CellWriter blocktype. + block NameHeaderWriter oftype CellWriter { + // 13. We utilize a syntax similar to spreadsheet programs. + // Cell ranges can be described using the keywords "cell", "row", "column", or "range" that indicate which + // cells are selected for the write action. + at: cell A1; + + // 14. For each cell we selected with the "at" property above, + // we can specify what value shall be written into the cell. + write: ["name"]; + } + + // 15. As a next step, we interpret the sheet as a table by adding structure. + // We define a valuetype per column that specifies the data type of the column. + // Rows that include values that are not valid according to the their valuetypes are dropped automatically. + block CarsTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "name" oftype text, + "mpg" oftype decimal, + "cyl" oftype integer, + "disp" oftype decimal, + "hp" oftype integer, + "drat" oftype decimal, + "wt" oftype decimal, + "qsec" oftype decimal, + "vs" oftype integer, + "am" oftype integer, + "gear" oftype integer, + "carb" oftype integer + ]; + } + + // 16. As a last step, we load the table into a sink, + // here into a sqlite file. + // The structural information of the table is used + // to generate the correct table. + block CarsLoader oftype SQLiteLoader { + table: "Cars"; + file: "./cars.sqlite"; + } + + // 17. Congratulations! + // You can now use the sink for your data analysis, app, + // or whatever you want to do with the cleaned data. +} +``` \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.18/user/examples/cars.md.license b/apps/docs/versioned_docs/version-0.0.18/user/examples/cars.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/examples/cars.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.18/user/examples/electric-vehicles.md b/apps/docs/versioned_docs/version-0.0.18/user/examples/electric-vehicles.md new file mode 100644 index 00000000..99398633 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/examples/electric-vehicles.md @@ -0,0 +1,150 @@ +--- +title: electric-vehicles +--- + +```jayvee +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// Example 2: Electric Vehicles +// Learning goals: +// - Understand further core concepts transforms and valuetypes +// - Understand how to construct a pipeline with multiple sinks +// - Understand the use of runtime parameters + +// 1. This Jayvee model describes a pipeline +// from a CSV file in the web +// to a SQLite file and a PostgreSQL db sink. +pipeline ElectricVehiclesPipeline { + // See here for meta-data of the data source + // https://catalog.data.gov/dataset/electric-vehicle-population-data/resource/fa51be35-691f-45d2-9f3e-535877965e69 + + // 2. At the top of a pipeline, we describe the + // structure of the pipeline. The first part until + // the ElectricRangeTransformer is a linear sequence + // of blocks. From there we can see a split into two + // parallel sequences that load the data in to two + // different sinks. + ElectricVehiclesHttpExtractor + -> ElectricVehiclesTextFileInterpreter + -> ElectricVehiclesCSVInterpreter + -> ElectricVehiclesTableInterpreter + -> ElectricRangeTransformer; + + ElectricRangeTransformer + -> ElectricVehiclesSQLiteLoader; + + ElectricRangeTransformer + -> ElectricVehiclesPostgresLoader; + + // 3. After the pipeline structure, we define the blocks used. + block ElectricVehiclesHttpExtractor oftype HttpExtractor { + url: "https://data.wa.gov/api/views/f6w7-q2d2/rows.csv?accessType=DOWNLOAD"; + } + + block ElectricVehiclesTextFileInterpreter oftype TextFileInterpreter { } + + block ElectricVehiclesCSVInterpreter oftype CSVInterpreter { } + + block ElectricVehiclesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + // 4. Here, a user-deifned valuetype is used to describe this column. + // The capital letter indicates that the valuetype is not builtin + // by convention. The valuetype itself is defined further below. + "VIN (1-10)" oftype VehicleIdentificationNumber10, + "County" oftype text, + "City" oftype text, + "State" oftype UsStateCode, + "Postal Code" oftype text, + "Model Year" oftype integer, + "Make" oftype text, + "Model" oftype text, + "Electric Vehicle Type" oftype text, + "Clean Alternative Fuel Vehicle (CAFV) Eligibility" oftype text, + "Electric Range" oftype integer, + "Base MSRP" oftype integer, + "Legislative District" oftype text, + "DOL Vehicle ID" oftype integer, + "Vehicle Location" oftype text, + "Electric Utility" oftype text, + "2020 Census Tract" oftype text, + ]; + } + + // 5. This block describes the application of a transform function + // taking a column as input and adding another computed column. + // The applied transform function is defined below and referenced + // by the "use" property. + block ElectricRangeTransformer oftype TableTransformer { + inputColumns: ["Electric Range"]; + outputColumn: "Electric Range (km)"; + use: MilesToKilometers; + } + + // 6. Here, we define a transform function, taking parameters + // as input ("from" keyword), and producing an output ("to" keyword). + // Inputs and outputs have to be further described by a valuetype. + transform MilesToKilometers { + from miles oftype decimal; + to kilometers oftype integer; + + // 7. In order to express what the transform function does, + // we assign an expression to the output. Values from the input and output of the transform can be referred to by name. + kilometers: round (miles * 1.609344); + } + + block ElectricVehiclesSQLiteLoader oftype SQLiteLoader { + table: "ElectricVehiclePopulationData"; + file: "./electric-vehicles.sqlite"; + } + + block ElectricVehiclesPostgresLoader oftype PostgresLoader { + // 8. The requires keyword allows us to define runtime parameters. + // These values have to be provided as environment variables when interpreting the Jayvee model. + host: requires DB_HOST; + port: requires DB_PORT; + username: requires DB_USERNAME; + password: requires DB_PASSWORD; + database: requires DB_DATABASE; + table: "ElectricVehiclePopulationData"; + } +} + +// 9. Below the pipeline, we model user-define valuetypes. +// We give them a speaking name and provide a base valuetype +// that this valuetype builts on. User-defined valuetypes always place additional constraints on existing valuetypes. +valuetype VehicleIdentificationNumber10 oftype text { + // 10. Valuetypes can be further refined by providing constraints. + constraints: [ + OnlyCapitalLettersAndDigits, + ExactlyTenCharacters, + ]; +} + +// 11. This constraint works on text valuetypes and requires values +// to match a given regular expression in order to be valid. +constraint OnlyCapitalLettersAndDigits on text: + value matches /^[A-Z0-9]*$/; + +constraint ExactlyTenCharacters on text: + value.length == 10; + +valuetype UsStateCode oftype text { + constraints: [ + UsStateCodeAllowlist, + ]; +} + +constraint UsStateCodeAllowlist on text: + value in [ + "AL", "AK", "AZ", "AR", "AS", "CA", "CO", "CT", "DE", "DC", + "FL", "GA", "GU", "HI", "ID", "IL", "IN", "IA", "KS", "KY", + "LA", "ME", "MD", "MA", "MI", "MN", "MS", "MO", "MT", "NE", + "NV", "NH", "NJ", "NM", "NY", "NC", "ND", "MP", "OH", "OK", + "OR", "PA", "PR", "RI", "SC", "SD", "TN", "TX", "TT", "UT", + "VT", "VA", "VI", "WA", "WV", "WI", "WY", + ]; + +``` \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.18/user/examples/electric-vehicles.md.license b/apps/docs/versioned_docs/version-0.0.18/user/examples/electric-vehicles.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/examples/electric-vehicles.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.18/user/examples/gtfs-rt.md b/apps/docs/versioned_docs/version-0.0.18/user/examples/gtfs-rt.md new file mode 100644 index 00000000..7610d8c1 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/examples/gtfs-rt.md @@ -0,0 +1,133 @@ +--- +title: gtfs-rt +--- + +```jayvee +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// Example 3: GTFS Realtime Data +// Learning goals: +// - Understand the construction of a csv file with multiple tables +// - Understand how to work with live data + +// 1. This Jayvee model describes a pipeline +// from a GTFS RT data source in the web +// to a SQLite file with multiple tables. +pipeline GtfsRTSimplePipeline { + + // 2. As you can see here, we have three independent + // sequences of pipes in this pipeline. + GTFSRTTripUpdateFeedExtractor + ->GtfsRTTripUpdateInterpreter + ->TripUpdateTableInterpreter + ->TripUpdateLoader; + + GTFSRTVehiclePositionFeedExtractor + ->GtfsRTVehiclePositionInterpreter + ->VehiclePositionTableInterpreter + ->VehicleLoader; + + GTFSRTAlertFeedExtractor + ->GtfsRTAlertInterpreter + ->AlertTableInterpreter + ->AlertLoader; + + // 3. We define a series of HttpExtractors that each pull data + // from an HTTP endpoint + block GTFSRTTripUpdateFeedExtractor oftype HttpExtractor { + url: "https://proxy.transport.data.gouv.fr/resource/bibus-brest-gtfs-rt-trip-update"; + } + + block GTFSRTVehiclePositionFeedExtractor oftype HttpExtractor { + url: "https://proxy.transport.data.gouv.fr/resource/bibus-brest-gtfs-rt-vehicle-position"; + } + + block GTFSRTAlertFeedExtractor oftype HttpExtractor { + url: "https://proxy.transport.data.gouv.fr/resource/bibus-brest-gtfs-rt-alerts"; + } + + // 4. In the next step, we use the domain-specific GtfsRTInterpreter + // to interpret the fetched files as sheets + block GtfsRTTripUpdateInterpreter oftype GtfsRTInterpreter { + entity: "trip_update"; + } + + block GtfsRTAlertInterpreter oftype GtfsRTInterpreter { + entity: "alert"; + } + + block GtfsRTVehiclePositionInterpreter oftype GtfsRTInterpreter { + entity: "vehicle"; + } + + // 5. Next, we interpret the sheets as tables + block TripUpdateTableInterpreter oftype TableInterpreter { + header: true; + columns:[ + "header.gtfs_realtime_version" oftype text, + "header.timestamp" oftype text, + "header.incrementality" oftype text, + "entity.id" oftype text, + "entity.trip_update.trip.trip_id" oftype text, + "entity.trip_update.trip.route_id" oftype text, + "entity.trip_update.stop_time_update.stop_sequence" oftype text, + "entity.trip_update.stop_time_update.stop_id" oftype text, + "entity.trip_update.stop_time_update.arrival.time" oftype text, + "entity.trip_update.stop_time_update.departure.time" oftype text, + ]; + } + + block VehiclePositionTableInterpreter oftype TableInterpreter { + header: true; + columns:[ + "header.gtfs_realtime_version" oftype text, + "header.timestamp" oftype text, + "header.incrementality" oftype text, + "entity.id" oftype text, + "entity.vehicle_position.vehicle_descriptor.id" oftype text, + "entity.vehicle_position.trip.trip_id" oftype text, + "entity.vehicle_position.trip.route_id" oftype text, + "entity.vehicle_position.position.latitude" oftype text, + "entity.vehicle_position.position.longitude" oftype text, + "entity.vehicle_position.timestamp" oftype text + ]; + } + + block AlertTableInterpreter oftype TableInterpreter { + header: true; + columns:[ + 'header.gtfs_realtime_version' oftype text, + 'header.timestamp' oftype text, + 'header.incrementality' oftype text, + 'entity.id' oftype text, + 'entity.alert.informed_entity.route_id' oftype text, + 'entity.alert.header_text' oftype text, + 'entity.alert.description_text' oftype text, + ]; + } + + // 6. Last, we load the tables into the same SQLite file. + // Each loader has to define a different table name. + // For working with live data, we use the property "dropTable: false" + // to append data instead of deleting the previous data. + block TripUpdateLoader oftype SQLiteLoader { + table: "gtfs-rt-trip_update"; + file: "./gtfs.sqlite"; + dropTable: false; + } + + block VehicleLoader oftype SQLiteLoader { + table: "gtfs-rt-vehicle_position"; + file: "./gtfs.sqlite"; + dropTable: false; + } + + block AlertLoader oftype SQLiteLoader { + table: "gtfs-rt-alert"; + file: "./gtfs.sqlite"; + dropTable: false; + } +} +``` \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.18/user/examples/gtfs-rt.md.license b/apps/docs/versioned_docs/version-0.0.18/user/examples/gtfs-rt.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/examples/gtfs-rt.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.18/user/examples/gtfs-static.md b/apps/docs/versioned_docs/version-0.0.18/user/examples/gtfs-static.md new file mode 100644 index 00000000..ca6434df --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/examples/gtfs-static.md @@ -0,0 +1,370 @@ +--- +title: gtfs-static +--- + +```jayvee +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// Example 4: GTFS Static Data +// Learning goals: +// - Understand how to work with file systems + +// 1. This Jayvee model describes a pipeline +// from a zip file in the GTFS format in the web +// to a joint SQLite file with multiple tables. +pipeline GtfsPipeline { + + // 2. The origin for multiple pipe sequences is a zip + // file. Each csv file in this zip is further processed + // by its own sequence of blocks and pipes. + GTFSSampleFeedExtractor -> ZipArchiveInterpreter; + + ZipArchiveInterpreter + -> AgencyFilePicker + -> AgencyTextFileInterpreter + -> AgencyCSVInterpreter + -> AgencyTableInterpreter + -> AgencyLoader; + + ZipArchiveInterpreter + -> CalendarDatesFilePicker + -> CalendarDatesTextFileInterpreter + -> CalendarDatesCSVInterpreter + -> CalendarDatesTableInterpreter + -> CalendarDatesLoader; + + ZipArchiveInterpreter + -> CalendarFilePicker + -> CalendarTextFileInterpreter + -> CalendarCSVInterpreter + -> CalendarTableInterpreter + -> CalendarLoader; + + ZipArchiveInterpreter + -> FareAttributesFilePicker + -> FareAttributesTextFileInterpreter + -> FareAttributesCSVInterpreter + -> FareAttributesTableInterpreter + -> FareAttributesLoader; + + ZipArchiveInterpreter + -> FareRulesFilePicker + -> FareRulesTextFileInterpreter + -> FareRulesCSVInterpreter + -> FareRulesTableInterpreter + -> FareRulesLoader; + + ZipArchiveInterpreter + -> FrequenciesFilePicker + -> FrequenciesTextFileInterpreter + -> FrequenciesCSVInterpreter + -> FrequenciesTableInterpreter + -> FrequenciesLoader; + + ZipArchiveInterpreter + -> RoutesFilePicker + -> RoutesTextFileInterpreter + -> RoutesCSVInterpreter + -> RoutesTableInterpreter + -> RoutesLoader; + + ZipArchiveInterpreter + -> ShapesFilePicker + -> ShapesTextFileInterpreter + -> ShapesCSVInterpreter + -> ShapesTableInterpreter + -> ShapesLoader; + + ZipArchiveInterpreter + -> StopTimesFilePicker + -> StopTimesTextFileInterpreter + -> StopTimesCSVInterpreter + -> StopTimesTableInterpreter + -> StopTimesLoader; + + ZipArchiveInterpreter + -> StopsFilePicker + -> StopsTextFileInterpreter + -> StopsCSVInterpreter + -> StopsTableInterpreter + -> StopsLoader; + + ZipArchiveInterpreter + -> TripsFilePicker + -> TripsTextFileInterpreter + -> TripsCSVInterpreter + -> TripsTableInterpreter + -> TripsLoader; + + // 3. As a first step, we download the zip file and interpret it. + block GTFSSampleFeedExtractor oftype HttpExtractor { + url: "https://developers.google.com/static/transit/gtfs/examples/sample-feed.zip"; + } + + block ZipArchiveInterpreter oftype ArchiveInterpreter { + archiveType: "zip"; + } + + // 4. Next, we pick several csv files (with the file extension ".txt") + // for further processing . + block AgencyFilePicker oftype FilePicker { + path: "/agency.txt"; + } + + block CalendarDatesFilePicker oftype FilePicker { + path: "/calendar_dates.txt"; + } + + block CalendarFilePicker oftype FilePicker { + path: "/calendar.txt"; + } + + block FareAttributesFilePicker oftype FilePicker { + path: "/fare_attributes.txt"; + } + + block FareRulesFilePicker oftype FilePicker { + path: "/fare_rules.txt"; + } + + block FrequenciesFilePicker oftype FilePicker { + path: "/frequencies.txt"; + } + + block RoutesFilePicker oftype FilePicker { + path: "/routes.txt"; + } + + block ShapesFilePicker oftype FilePicker { + path: "/shapes.txt"; + } + + block StopTimesFilePicker oftype FilePicker { + path: "/stop_times.txt"; + } + + block StopsFilePicker oftype FilePicker { + path: "/stops.txt"; + } + + block TripsFilePicker oftype FilePicker { + path: "/trips.txt"; + } + + // 5. The rest of the pipeline follows the usual pattern. + block AgencyTextFileInterpreter oftype TextFileInterpreter { } + block CalendarDatesTextFileInterpreter oftype TextFileInterpreter { } + block CalendarTextFileInterpreter oftype TextFileInterpreter { } + block FareAttributesTextFileInterpreter oftype TextFileInterpreter { } + block FareRulesTextFileInterpreter oftype TextFileInterpreter { } + block FrequenciesTextFileInterpreter oftype TextFileInterpreter { } + block RoutesTextFileInterpreter oftype TextFileInterpreter { } + block ShapesTextFileInterpreter oftype TextFileInterpreter { } + block StopTimesTextFileInterpreter oftype TextFileInterpreter { } + block StopsTextFileInterpreter oftype TextFileInterpreter { } + block TripsTextFileInterpreter oftype TextFileInterpreter { } + block AgencyCSVInterpreter oftype CSVInterpreter { } + block CalendarDatesCSVInterpreter oftype CSVInterpreter { } + block CalendarCSVInterpreter oftype CSVInterpreter { } + block FareAttributesCSVInterpreter oftype CSVInterpreter { } + block FareRulesCSVInterpreter oftype CSVInterpreter { } + block FrequenciesCSVInterpreter oftype CSVInterpreter { } + block RoutesCSVInterpreter oftype CSVInterpreter { } + block ShapesCSVInterpreter oftype CSVInterpreter { } + block StopTimesCSVInterpreter oftype CSVInterpreter { } + block StopsCSVInterpreter oftype CSVInterpreter { } + block TripsCSVInterpreter oftype CSVInterpreter { } + + block AgencyTableInterpreter oftype TableInterpreter { + header: true; + columns:[ + "agency_id" oftype text, //Conditional columns are considered as required + "agency_name" oftype text, + "agency_url" oftype text, + "agency_timezone" oftype text + ]; + } + + block CalendarDatesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "service_id" oftype text, + "date" oftype text, + "exception_type" oftype text + ]; + } + + block CalendarTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "service_id" oftype text, + "monday" oftype text, + "tuesday" oftype text, + "wednesday" oftype text, + "thursday" oftype text, + "friday" oftype text, + "saturday" oftype text, + "sunday" oftype text, + "start_date" oftype text, + "end_date" oftype text + ]; + } + + block FareAttributesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "fare_id" oftype text, + "price" oftype text, + "currency_type" oftype text, + "payment_method" oftype text, + "transfers" oftype text, + "transfer_duration" oftype text + ]; + } + + block FareRulesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "fare_id" oftype text, + "route_id" oftype text, + "origin_id" oftype text, + "destination_id" oftype text, + "contains_id" oftype text + ]; + } + + block FrequenciesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "trip_id" oftype text, + "start_time" oftype text, + "end_time" oftype text, + "headway_secs" oftype text + ]; + } + + block RoutesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "route_id" oftype text, + "agency_id" oftype text, + "route_short_name" oftype text, + "route_long_name" oftype text, + "route_desc" oftype text, + "route_type" oftype text, + "route_url" oftype text, + "route_color" oftype text, + "route_text_color" oftype text + ]; + } + + block ShapesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "shape_id" oftype text, + "shape_pt_lat" oftype text, + "shape_pt_lon" oftype text, + "shape_pt_sequence" oftype text, + "shape_dist_traveled" oftype text + ]; + } + + block StopTimesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "trip_id" oftype text, + "arrival_time" oftype text, + "departure_time" oftype text, + "stop_id" oftype text, + "stop_sequence" oftype text, + "stop_headsign" oftype text, + "pickup_type" oftype text, + "drop_off_time" oftype text, + "shape_dist_traveled" oftype text + ]; + } + + block StopsTableInterpreter oftype TableInterpreter { + header: true; + columns:[ + "stop_id" oftype text, + "stop_name" oftype text, + "stop_desc" oftype text, + "stop_lat" oftype text, + "stop_lon" oftype text, + "zone_id" oftype text, + "stop_url" oftype text + ]; + } + + block TripsTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "route_id" oftype text, + "service_id" oftype text, + "trip_id" oftype text, + "trip_headsign" oftype text, + "direction_id" oftype text, + "block_id" oftype text, + "shape_id" oftype text + ]; + } + + block AgencyLoader oftype SQLiteLoader { + table: "agency"; + file: "./gtfs.sqlite"; + } + + block CalendarDatesLoader oftype SQLiteLoader { + table: "calendar_dates"; + file: "./gtfs.sqlite"; + } + + block CalendarLoader oftype SQLiteLoader { + table: "calendar"; + file: "./gtfs.sqlite"; + } + + block FareAttributesLoader oftype SQLiteLoader { + table: "fare_attributes"; + file: "./gtfs.sqlite"; + } + + block FareRulesLoader oftype SQLiteLoader { + table: "fare_rules"; + file: "./gtfs.sqlite"; + } + + block FrequenciesLoader oftype SQLiteLoader { + table: "frequencies"; + file: "./gtfs.sqlite"; + } + + block RoutesLoader oftype SQLiteLoader { + table: "routes"; + file: "./gtfs.sqlite"; + } + + block ShapesLoader oftype SQLiteLoader { + table: "shapes"; + file: "./gtfs.sqlite"; + } + + block StopTimesLoader oftype SQLiteLoader { + table: "stop_times"; + file: "./gtfs.sqlite"; + } + + block StopsLoader oftype SQLiteLoader { + table: "stops"; + file: "./gtfs.sqlite"; + } + + block TripsLoader oftype SQLiteLoader { + table: "trips"; + file: "./gtfs.sqlite"; + } +} +``` \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.18/user/examples/gtfs-static.md.license b/apps/docs/versioned_docs/version-0.0.18/user/examples/gtfs-static.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/examples/gtfs-static.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.18/user/examples/test.md b/apps/docs/versioned_docs/version-0.0.18/user/examples/test.md new file mode 100644 index 00000000..89a0ff2c --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/examples/test.md @@ -0,0 +1,25 @@ +--- +title: test +--- + +```jayvee +/* +Interprets an input file as a csv-file containing string-values delimited by `delimiter` and outputs a `Sheet`. + +@example Interprets an input file as a csv-file containing string-values delimited by `;` and outputs `Sheet`. +block AgencyCSVInterpreter oftype CSVInterpreter { + delimiter: ";"; + } +*/ +builtin blocktype CSVInterpreter { + input default oftype TextFile; + output default oftype Sheet; + + // The delimiter for values in the CSV file. + property delimiter oftype text: ','; + // The enclosing character that may be used for values in the CSV file. + property enclosing oftype text: ''; + // The character to escape enclosing characters in values. + property enclosingEscape oftype text: ''; +} +``` \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.18/user/examples/test.md.license b/apps/docs/versioned_docs/version-0.0.18/user/examples/test.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/examples/test.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.18/user/examples/workbooks-xlsx.md b/apps/docs/versioned_docs/version-0.0.18/user/examples/workbooks-xlsx.md new file mode 100644 index 00000000..a46e886a --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/examples/workbooks-xlsx.md @@ -0,0 +1,97 @@ +--- +title: workbooks-xlsx +--- + +```jayvee +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// Example 1: LightTrapping +// Learning goals: +// - Understand how to work with XLSX files and workbooks + +// 1. This Jayvee model describes a pipeline +// from a XLSX file with multiple Sheets in the web +// to a SQLite file sink. +pipeline LightTrappingSiliconSolarCellsPipeline { + // 2. We directly get the xlsx file from the web via the HttpExtractor + // The data is provided under CC BY-SA 4.0 + // Saive, Rebecca (2023). Data supporting the publication: + // Light trapping in thin silicon solar cells: a review on fundamentals and technologies. + // 4TU.ResearchData. Dataset. https://doi.org/10.4121/14554815.v1 + block LightTrappingSiliconSolarCellsExtractor oftype HttpExtractor { + url: "https://figshare.com/ndownloader/files/27923598"; + } + + // 3. The incoming file is interpreted as a XLSX file and transformed into a Workbook + // Workbooks contain at least 1 Sheet. Every sheet has a unique name. + block LightTrappingSiliconSolarCellsTextXLSXInterpreter oftype XLSXInterpreter { + + } + + // 4.1 Here, we pick one sheet with the name 'RefractiveIndexSi GaAs' from the Workbook to use within our pipeline. + // The output type from SheetPicker is Sheet, which was already introduced in the cars example + block LightTrappingSiliconSolarCellsSheetpicker oftype SheetPicker { + sheetName: 'RefractiveIndexSi GaAs'; + } + + block NameHeaderWriter oftype CellWriter { + at: range F1:L1; + write: ["F","G","nm","wl","n2", "k2", "alpha (cm-1)2"]; + } + + block LightTrappingSiliconSolarCellsTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "Wavelength" oftype integer, + "Wavelength (µm)" oftype decimal, + "n" oftype decimal, + "k" oftype text, + "alpha (cm-1)" oftype text, + "nm" oftype decimal, + "n2" oftype text, + "k2" oftype decimal, + "alpha (cm-1)2" oftype decimal + ]; + } + + block LightTrappingSiliconSolarCellsLoader oftype SQLiteLoader { + table: "LightTrappingSiliconSolarCells"; + file: "./LightTrappingSiliconSolarCells.sqlite"; + } + + // 4.2 Here, we pick another sheet named 'Wavelength thickness trapping' from the Workbook + block SecondLightTrappingSiliconSolarCellsSheetpicker oftype SheetPicker { + sheetName: 'Wavelength thickness trapping'; + } + + block SecondLightTrappingSiliconSolarCellsTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "n" oftype decimal, + "Wavelength (µm)" oftype decimal, + ]; + } + + block SecondLightTrappingSiliconSolarCellsLoader oftype SQLiteLoader { + + table: "SecondLightTrappingSiliconSolarCells"; + file: "./LightTrappingSiliconSolarCells.sqlite"; + } + + LightTrappingSiliconSolarCellsExtractor + -> LightTrappingSiliconSolarCellsTextXLSXInterpreter + -> LightTrappingSiliconSolarCellsSheetpicker + -> NameHeaderWriter + -> LightTrappingSiliconSolarCellsTableInterpreter + -> LightTrappingSiliconSolarCellsLoader; + + // 5. Once the XLSX file is interpreted, we can split the pipeline and + // work separately on the different sheets from our input file + LightTrappingSiliconSolarCellsTextXLSXInterpreter + -> SecondLightTrappingSiliconSolarCellsSheetpicker + -> SecondLightTrappingSiliconSolarCellsTableInterpreter + -> SecondLightTrappingSiliconSolarCellsLoader; +} +``` \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.18/user/examples/workbooks-xlsx.md.license b/apps/docs/versioned_docs/version-0.0.18/user/examples/workbooks-xlsx.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/examples/workbooks-xlsx.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.18/user/expressions.md b/apps/docs/versioned_docs/version-0.0.18/user/expressions.md new file mode 100644 index 00000000..99e612a5 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/expressions.md @@ -0,0 +1,80 @@ +--- +sidebar_position: 7 +--- + +# Expressions + +Expressions in Jayvee are arbitrarily nested statements. They consist of: +- literals (e.g., numbers `5` or strings `"Example"`) +- variables (e.g., declared by `from` properties in [Transforms](./transforms.md)) +- operators (e.g., `*` or `sqrt`) + +Expressions get evaluated at runtime by the interpreter to a [Built-in ValueType](./valuetypes/builtin-valuetypes). + +### Example + +The following expression is evaluated to the `integer` `10`: `(2 + 3) * 2` + +The following expression is evaluated to the `integer` `3`: `floor (3.14)` + +The following expression is evaluated to the `boolean` `true`: `"Example" == "Example"` + +### List of Operators + +#### Arithmetics (binary operators) +- `+` for addition, e.g., `5 + 3` evaluates to `8` +- `-` for subtraction, e.g., `5 - 3` evaluates to `2` +- `*` for multiplication, e.g., `5 * 3` evaluates to `15` +- `/` for division, e.g., `6 / 3` evaluates to `2` +- `%` for modulo, e.g., `5 % 3` evaluates to `2` +- `pow` for power, e.g., `2 pow 3` evaluates to `8` +- `root` for root, e.g., `27 root 3` evaluates to `3` + +#### Arithmetics (unary operators) +- `+` for positive signing, e.g., `+5` evaluates to `5` +- `-` for negative signing, e.g., `-5` evaluates to `-5` +- `sqrt` for square root, e.g., `sqrt 9` evaluates to `3` +- `foor` for flooring a number, e.g., `floor 5.3` evaluates to `5` +- `ceil` for ceiling a number, e.g., `floor 5.3` evaluates to `6` +- `round` for rounding a number, e.g., `floor 5.3` evaluates to `5` + +#### Relational (binary operators) +- `<` for smaller, e.g., `3 < 3` evaluates to `false` +- `<=` for smaller or equal, e.g., `3 <= 3` evaluates to `true` +- `>` for greater, e.g., `3 > 3` evaluates to `false` +- `>=` for greater or equal, e.g., `3 >= 3` evaluates to `true` +- `==` for equal, e.g., `3 == 3` evaluates to `true` +- `!=` for not equal, e.g., `3 != 3` evaluates to `false` + +#### Logical (binary operators) +- `and` for a logical and (both need to be true to evaluate to true) +- `or` for a logical or (at least left or right needs to be true to evaluate to true) +- `xor` for a logical xor (either left or right needs to be true to evaluate to true) + +#### Logical (unary operators) +- `not` for logical negation, `not true` evaluates to `false` + +#### Others (binary operators) +- `matches` for a regex match, e.g., `"A07" matches /^[A-Z0-9]*$/` evaluates to `true` +- `in` for inclusion in an array, e.g., `"a" in ["a", "b", "c"]` evaluates to `true` + +### Operator Details + +#### `in` Operator + +The `in` operator checks whether a value is included in a collection of values. For example: + +```jayvee +4.5 in [3, 6.5] // evaluates to false +3 in [3.0, 6.5] // evaluates to true +"a" in ["a", "b", "c"] // evaluates to true +``` + +The operator supports `text`, `integer` and `decimal` values as operands. The compatibility of left and right operand types follows these rules: +- For the `in` operator we have a type for the needle (left operand) and a type for the elements in the haystack (right operand). +- There is an automated type conversion as long as it is lossless and clearly defined (integer to decimal as of now). +- We allow any combination of operands that has either: (i) An automated type conversion from needle type (left operand) to the type of the elements in the haystack (right operand), or (ii) the other way around. + + +### Further reading +For a deeper documentation of how expressions and operators work internally, refer to the [developer docs](../dev/04-guides/04-expressions-and-operators.md). \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.18/user/expressions.md.license b/apps/docs/versioned_docs/version-0.0.18/user/expressions.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/expressions.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.18/user/intro.md b/apps/docs/versioned_docs/version-0.0.18/user/intro.md new file mode 100644 index 00000000..2832707c --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/intro.md @@ -0,0 +1,95 @@ +--- +sidebar_position: 1 +--- + +# Introduction to Jayvee + +Jayvee is a domain-specific language (DSL) for data engineering - the cleaning and preprocessing of data for later activities like data science or machine learning. You can use Jayvee to **model an ETL pipeline** and the command-line interpreter to **run the ETL pipeline** on your local machine. + +## Installation + +Install the interpreter via `npm`. You will need a **nodejs version >= 17.0.0**. + +```bash +npm install -g @jvalue/jayvee-interpreter +``` + +You can install a specific version using the `@`-syntax, e.g., version `0.0.17`: +```bash +npm install -g @jvalue/jayvee-interpreter@0.0.17 +``` + +## Update + +Updating the interpreter is done by reinstalling it using `npm`. Make sure to also update the [VSCode plugin](#vscode-plugin) to match the installed interpreter if you use it. + +```bash +npm install -g @jvalue/jayvee-interpreter +``` + +## Usage + +### Show help + +```console +jv -h +``` + +### Run a `.jv` file + +```console +jv <file> +``` + +Run with **additional debug output**: + +```console +jv <file> -d +``` + + +With **runtime parameters**: + +```console +jv -e <param>=<value> -e <param>=<value> ... <file> +``` + +### Debug a `.jv` file + +Print debugging is further configured by the parameters `--debug-granularity` and `--debug-target`. + +```console +jv <file> -d -dg peek +``` +The value of the parameter `--debug-granularity` (short `-dg`) can have the following values: +- `peek` to log a short summary, including a small subset of data +- `exhaustive` to log a summary, including the full data +- `minimal` to log a summary, including no additional data (default). +To see logs, debugging has to be enabled using the `-d` flag. + +```console +jv <file> -d --debug-granularity peek +``` +The parameter `--debug-target` (short `-dt`) allows to specify which blocks should be logged for debugging. Separate block names by comma if multiple blocks are targeted. All blocks are logged if the parameter is omitted. +```console +jv <file> -d --debug-granularity peek --debug-target MyExtractorBlock,MySinkBlock +``` + + +## Examples + +You can find multiple examples [here](https://github.com/jvalue/jayvee/tree/main/example). Copy them to your local file system and execute them with the `jv` command on your command line (see [usage](#usage)). + + +## VSCode Plugin + +To set up Jayvee locally in VS Code, you need to install the latest Jayvee VS Code extension. +To install the most recent extension, go to our [latest release](https://github.com/jvalue/jayvee/releases/latest) +and download the `jayvee.vsix` file from the release assets. +Next, go to [this page](https://code.visualstudio.com/docs/editor/extension-marketplace#_install-from-a-vsix) and +follow the instructions for installing the downloaded extension. + +## Troubleshooting + +1. Error `structuredClone is not defined` + * Please make sure you use node version 17+. diff --git a/apps/docs/versioned_docs/version-0.0.18/user/intro.md.license b/apps/docs/versioned_docs/version-0.0.18/user/intro.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/intro.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.18/user/runtime-parameters.md b/apps/docs/versioned_docs/version-0.0.18/user/runtime-parameters.md new file mode 100644 index 00000000..bbdab1ed --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/runtime-parameters.md @@ -0,0 +1,27 @@ +--- +sidebar_position: 9 +--- + +# Runtime Parameters + +Property values in Jayvee can be assigned to `values` or left open for later configuration via `runtime parameters`. + +## Syntax + +Runtime parameters are indicated by the `requires` keyword, followed by the identifier of the parameter. Example + +```jayvee +block CarsLoader oftype SQLiteLoader { + table: "Cars"; + file: requires CARS_SQLITE_FILE; +} +``` + +## CLI + +The interpreter CLI has to define all existing runtime parameters for execution. +Use the CLI flag `-e` to to define them as key-value pairs of identifier and value. + +```console +jv -e <param>=<value> -e <param>=<value> ... <file> +``` diff --git a/apps/docs/versioned_docs/version-0.0.18/user/runtime-parameters.md.license b/apps/docs/versioned_docs/version-0.0.18/user/runtime-parameters.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/runtime-parameters.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.18/user/transforms.md b/apps/docs/versioned_docs/version-0.0.18/user/transforms.md new file mode 100644 index 00000000..bf3dfd7d --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/transforms.md @@ -0,0 +1,77 @@ +--- +sidebar_position: 8 +--- + +# Transforms + +Transforms are a concept in Jayvee to define the transformation of individual values. +They are similar to functions in programming languages, i.e. they perform computations on some input values and produce output values. Transform work by mapping input values to outputs using [expressions](./expressions.md). + +:::info Important + +Up to version `0.0.16`, we only supported a single input for transformers! + +::: + +:::info Important + +In its current state, Jayvee only supports a arbitrary numbers of inputs and a single output for transforms. +For the future, it is planned to support arbitrary numbers for outputs as well. + +::: + + +## Syntax + +The general syntax of transforms looks like this: + +```jayvee +transform <name> { + from <inputName> oftype <inputValuetype>; + to <outputName> oftype <outputValuetype>; + + <outputName>: <expression>; +} +``` + +The `transform` keyword is used to define a transform and give it a name. +The curly braces denote the body of the transform. + +The body first contains the definitions of input and output ports. +Input ports are defined using the `from` keyword whereas output ports use the `to` keyword. +Next, they are given a name and, after the `oftype` keyword, typed with a valuetype. + +Below, there needs to be an output assignment for each output port. +The output assignment defines how a particular output value is computed. +They consist of the name of an output port, followed by a `:`. +Next, an [expression](./expressions.md) specifies how the output value shall be computed. +Names of input ports can be used in such an expression to refer to input values. + +### Example + +The following transform converts temperature values from degree Celsius to Kelvin: + +```jayvee +transform CelsiusToKelvin { + from tempCelsius oftype decimal; + to tempKelvin oftype decimal; + + tempKelvin: tempCelsius + 273.15; +} +``` + +The following transform converts a text based status into a boolean value, `true` if the text is `Active`, `false` for any other value: + +```jayvee +transform StatusToBoolean { + from statusText oftype text; + to statusBoolean oftype boolean; + + statusBoolean: statusText == "Active"; +} +``` + +## Applying transforms to table columns + +Transforms can be applied to columns of a table. +Please refer to the documentation of the [`TableTransformer` block type](./block-types/TableTransformer.md) to find out how. diff --git a/apps/docs/versioned_docs/version-0.0.18/user/transforms.md.license b/apps/docs/versioned_docs/version-0.0.18/user/transforms.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/transforms.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.18/user/valuetypes/_category_.json b/apps/docs/versioned_docs/version-0.0.18/user/valuetypes/_category_.json new file mode 100644 index 00000000..ed1ce95f --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/valuetypes/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Valuetypes", + "position": 5, + "link": { + "type": "generated-index", + "description": "Jayvee supports these different kinds of valuetypes." + } +} diff --git a/apps/docs/versioned_docs/version-0.0.18/user/valuetypes/_category_.json.license b/apps/docs/versioned_docs/version-0.0.18/user/valuetypes/_category_.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/valuetypes/_category_.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.0.18/user/valuetypes/builtin-valuetypes.md b/apps/docs/versioned_docs/version-0.0.18/user/valuetypes/builtin-valuetypes.md new file mode 100644 index 00000000..0bc36dcb --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/valuetypes/builtin-valuetypes.md @@ -0,0 +1,98 @@ +--- +title: Built-in Valuetypes +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +# Description + +For an introduction to valuetypes, see the [Core Concepts](../core-concepts). +Built-in valuetypes come with the basic version of Jayvee. +They are the basis for more restricted [Primitive Valuetypes](./primitive-valuetypes) +that fullfil [Constraints](./primitive-valuetypes#constraints). + +# Available built-in valuetypes + +## Boolean + +### Description + +A boolean value. +Examples: true, false + +### Example 1 + +```jayvee +block ExampleTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "columnName" oftype boolean + ]; +} +``` + +A block of type `TableInterpreter` that + interprets data in the column `columnName` as `boolean`. + +## Decimal + +### Description + +A decimal value. +Example: 3.14 + +### Example 1 + +```jayvee +block ExampleTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "columnName" oftype decimal + ]; +} +``` + +A block of type `TableInterpreter` that + interprets data in the column `columnName` as `decimal`. + +## Integer + +### Description + +An integer value. +Example: 3 + +### Example 1 + +```jayvee +block ExampleTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "columnName" oftype integer + ]; +} +``` + +A block of type `TableInterpreter` that + interprets data in the column `columnName` as `integer`. + +## Text + +### Description + +A text value. +Example: "Hello World" + +### Example 1 + +```jayvee +block ExampleTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "columnName" oftype text + ]; +} +``` + +A block of type `TableInterpreter` that + interprets data in the column `columnName` as `text`. diff --git a/apps/docs/versioned_docs/version-0.0.18/user/valuetypes/builtin-valuetypes.md.license b/apps/docs/versioned_docs/version-0.0.18/user/valuetypes/builtin-valuetypes.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/valuetypes/builtin-valuetypes.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.18/user/valuetypes/primitive-valuetypes.md b/apps/docs/versioned_docs/version-0.0.18/user/valuetypes/primitive-valuetypes.md new file mode 100644 index 00000000..92400a53 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/valuetypes/primitive-valuetypes.md @@ -0,0 +1,48 @@ +--- +sidebar_position: 2 +--- +# Primitive ValueTypes + +`Primitive ValueTypes` are based on `Built-in ValueTypes` and use a collection of constraints to restrict the range of valid values. +Such constraints are implicitly connected via a logical `AND` relation. +Note that the `Constraints` need to be applicable to the base-type of the `ValueType` - indicated by the identifier after the keyword `oftype`: + +```jayvee +valuetype GasFillLevel oftype integer { + constraints: [ GasFillLevelRange ]; +} +``` + + +## Constraints + +`Constraints` for `ValueTypes` declare the validity criteria that each concrete value is checked against. + +### Syntax 1: Expression syntax + +The syntax of expression-based `Constraints` uses an expression that evaluates to `true` or `false` for the given `value`. The type of the values the expression is working in is indicated ofter the keyword `on`: + +```jayvee +constraint GasFillLevelRange on decimal: + value >= 0 and value <= 100; +``` + +Refer to the [Expression documentation](../expressions.md) for further reading on expressions. + + +### Syntax 2: Block-like syntax + +The syntax of `Constraints` is similar to the syntax of `Blocks`. +The availability of property keys and their respective `ValueTypes` is determined by the type of the `Constraint` - indicated by the identifier after the keyword `oftype`: + +```jayvee +constraint GasFillLevelRange oftype RangeConstraint { + lowerBound: 0; + lowerBoundInclusive: true; + upperBound: 100; + upperBoundInclusive: true; +} +``` + +Note that the type of `Constraint` also determines its applicability to `ValueTypes`. +For instance, a `RangeConstraint` can only be applied to the numerical types `integer` and `decimal`. \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.0.18/user/valuetypes/primitive-valuetypes.md.license b/apps/docs/versioned_docs/version-0.0.18/user/valuetypes/primitive-valuetypes.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.0.18/user/valuetypes/primitive-valuetypes.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.1.0/dev/01-intro.md b/apps/docs/versioned_docs/version-0.1.0/dev/01-intro.md new file mode 100644 index 00000000..fef3c08e --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/dev/01-intro.md @@ -0,0 +1,52 @@ +--- +sidebar_position: 1 +--- + +# Introduction for Jayvee Developers + +## How to contribute + +In order to contribute to the Jayvee project, please have a look at our [contribution guide](https://github.com/jvalue/jayvee/blob/main/CONTRIBUTING.md). Before planning a contribution, please read the [design principles](./05-design-principles.md) and consider if your changes fit the vision expressed there. + +The overall project is licensed under the `AGPL-3.0-only` license and is compliant to the [REUSE Specification](https://reuse.software/spec/) by the [Free Software Foundation Europe](https://fsfe.org/). +Because of this, contributions are required to adhere to the license and follow that specification. +More details on this topic can be found [here](./02-dev-processes/03-licensing-and-copyright.md). + +And last but not least, please read and follow our [code of conduct](https://github.com/jvalue/jayvee/blob/main/CODE_OF_CONDUCT.md). + +## Project overview + +The Jayvee repository is located [here](https://github.com/jvalue/jayvee) on GitHub. +It uses an [Nx mono-repository](https://nx.dev/) setup and contains all related projects, most notably the Jayvee language server and interpreter. +Please have a look at the [architecture overview](./03-architecture-overview.md) for more details. + +## Prerequisites + +Node.js and npm are required for development. +It is recommended to use their LTS version to avoid any potential compatibility issues. +Also, refer to this [quick start guide](https://github.com/jvalue/jayvee#development-quickstart) for developers. + +In order to run the Jayvee VS Code extension during development, VS Code is required as IDE. +Using VS Code also allows installing the [Langium VS Code extension](https://marketplace.visualstudio.com/items?itemName=langium.langium-vscode) for better IDE support when working with Langium grammar files. + +## Resources for getting started + +### Langium + +- [**Langium documentation**](https://langium.org/docs/) +- Practical examples using Langium: + - [Langium examples](https://github.com/langium/langium/tree/main/examples): Official example languages by Langium + - [Langium's grammar language](https://github.com/langium/langium/tree/main/packages/langium): The implementation of Langium's own grammar language + - [Language server for SQL](https://github.com/langium/langium-sql): An implementation of a language server for SQL + - [MiniLogo DSL](https://github.com/langium/langium-minilogo): A domain-specific language for drawing pictures + - [Lox language](https://github.com/langium/langium-lox): An implementation of the Lox scripting language (also see the "Crafting Interpreters" book below) + - [SimpleUI DSL](https://github.com/TypeFox/langium-ui-framework): A domain-specific language for generating user interfaces + - [STPA-DSL](https://github.com/kieler/stpa): A domain-specific language for System-Theoretic Process Analysis +- [Langium playground](https://langium.org/playground/): A tool for testing Langium grammars and visualizing resulting ASTs +- [Typefox blog](https://www.typefox.io/blog/): A blog by the company behind Langium, mostly posting Langium-related content. + +### Building compilers / interpreters / DSLs + +- [Crafting Interpreters](https://craftinginterpreters.com/contents.html): A book by Robert Nystrom on implementing an interpreter for the Lox scripting language +- [DSL Engineering](https://voelter.de/dslbook/markusvoelter-dslengineering-1.0.pdf): A book by Markus Voelter on designing, implementing and using domain-specific languages +- [Awesome Compilers](https://github.com/aalhour/awesome-compilers#readme): A list featuring many resources on compilers and interpreters diff --git a/apps/docs/versioned_docs/version-0.1.0/dev/01-intro.md.license b/apps/docs/versioned_docs/version-0.1.0/dev/01-intro.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/dev/01-intro.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.1.0/dev/02-dev-processes/01-rfc-process.md b/apps/docs/versioned_docs/version-0.1.0/dev/02-dev-processes/01-rfc-process.md new file mode 100644 index 00000000..07444085 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/dev/02-dev-processes/01-rfc-process.md @@ -0,0 +1,13 @@ +--- +title: Language Design Process (RFCs) +sidebar_position: 1 +--- + +We use RFCs to discuss changes to the language before implementing them. You can have a look at all closed (accepted / rejected) RFCs [here](https://github.com/jvalue/jayvee/pulls?q=is%3Apr+is%3Aclosed+RFC+), and all RFCs under discussion [here](https://github.com/jvalue/jayvee/pulls?q=is%3Apr+is%3Aopen+RFC). + +If you want to contribute an RFC please follow these steps: +1. Make a copy of the **template** at `rfc/0000-rfc-template.md` and place it into the `rfc` folder. +2. Create a draft for the RFC on a new branch. Follow the `TODOs` in template to do so. +3. Open a pull request with prefix `RFC <number>` in the title. +4. Address the reviews. Consider opening a new PR if major things need to be addressed and the discussion log becomes too confusing. +5. Once accepted, create an issue with the necessary steps to implement the RFC. \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.1.0/dev/02-dev-processes/01-rfc-process.md.license b/apps/docs/versioned_docs/version-0.1.0/dev/02-dev-processes/01-rfc-process.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/dev/02-dev-processes/01-rfc-process.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.1.0/dev/02-dev-processes/02-debug-vs-code-extension.md b/apps/docs/versioned_docs/version-0.1.0/dev/02-dev-processes/02-debug-vs-code-extension.md new file mode 100644 index 00000000..5fbcf07c --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/dev/02-dev-processes/02-debug-vs-code-extension.md @@ -0,0 +1,25 @@ +--- +title: Debugging via the VS Code extension +sidebar_position: 2 +--- + +The VS Code extension of Jayvee can be used to interactively debug the language server. +During development, when using VS Code as IDE, another instance of VS Code can be opened which has the most recent, locally built VS Code extension loaded. +This can be achieved using the launch configuration `Run extension` the `Run and Debug` side-menu of VS Code or by pressing the `F5` key if that launch configuration is already selected there. + +Note that there is no file watching mechanism involved. +So in order to reflect changes to the source code, the additional VS Code instance has to be **closed and reopened** as described above. + +## How to attach a debugger + +1. Use the launch configuration `Run extension` to open the additional VS Code instance with the extension loaded +2. Use the launch configuration `Attach to Language Server` to attach the debugger + +Any set breakpoints should now be marked as active and pause the execution once they are reached. + +## How to view logs of the language server + +In the additional VS Code instance, it is possible to view the logs of the language server. +They might also be helpful for debugging since any uncaught errors are logged with their stack trace by the Langium framework. + +To view the logs, open the bottom panel called `Output` and select `Jayvee` in the dropdown menu. diff --git a/apps/docs/versioned_docs/version-0.1.0/dev/02-dev-processes/02-debug-vs-code-extension.md.license b/apps/docs/versioned_docs/version-0.1.0/dev/02-dev-processes/02-debug-vs-code-extension.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/dev/02-dev-processes/02-debug-vs-code-extension.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.1.0/dev/02-dev-processes/03-licensing-and-copyright.md b/apps/docs/versioned_docs/version-0.1.0/dev/02-dev-processes/03-licensing-and-copyright.md new file mode 100644 index 00000000..201e21ca --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/dev/02-dev-processes/03-licensing-and-copyright.md @@ -0,0 +1,69 @@ +--- +title: Licensing and copyright +sidebar_position: 3 +--- + +The [Jayvee repository](https://github.com/jvalue/jayvee) is REUSE compliant, meaning that it fulfills the +[REUSE Specification](https://reuse.software/spec/) by the [Free Software Foundation Europe](https://fsfe.org/) (FSFE). +This makes clear under which license each project file is licensed under and who owns the copyright, not only for +humans but also for machines. + +This is done by explicitly adding copyright and licensing information to each file of the project. This is achieved +by either using a comment header or a separate `*.license` file in case comments are not possible. + +See <https://reuse.software/> more information. + +## What license is used in this project and who is the copyright holder? + +The entire project is licensed under [AGPL-3.0-only](https://spdx.org/licenses/AGPL-3.0-only.html), the +license file can be found [here](https://github.com/jvalue/jayvee/blob/main/LICENSES/AGPL-3.0-only.txt). +The copyright holder is the [Friedrich-Alexander-Universität Erlangen-Nürnberg](https://www.fau.eu/). + +## How to submit a REUSE compliant contribution? + +In case you want to contribute to the project, you will need to ensure that all of your contributed files are REUSE +compliant. In order to achieve this, you need to add a key-value pair for both copyright and licensing information +following this schema: + +``` +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only +``` + +In case a file allows comments, use single-line comments to add the copyright and licensing information at the top +of the file. Otherwise, create a corresponding `*.license` file with the text above as its content. You can have a +look at some existing project files to get an impression on it is done in practice. + +For files with common file extensions, you can use the [reuse CLI tool](https://github.com/fsfe/reuse-tool) to add +licensing and copyright information automatically. + +For more details, you can have a look at the [Getting Started tutorial](https://reuse.software/tutorial/) on the REUSE +website. + +## How to validate REUSE compliance? + +When you make a contribution and open a new pull request, the CI checks whether your contribution is REUSE compliant +using the [reuse CLI tool](https://github.com/fsfe/reuse-tool). + +In order to validate REUSE compliance in your local development environment, you have to install the +[reuse CLI tool](https://github.com/fsfe/reuse-tool) and run the following command in the projects' root folder: + +```bash +reuse lint +``` + +You can also set up a pre-commit hook, so the above command is run before each commit. +See [here](https://reuse.readthedocs.io/en/latest/readme.html#run-as-pre-commit-hook) for details on how to set it up. + +## How to hide `*.license` files in IDEs + +During development, the file explorer of your IDE may be cluttered due to the numerous `*.license` files in the +project. Luckily, most IDEs allow hiding certain files, e.g. by specifying a pattern to exclude them from the +explorer. + +Below, you can find instructions on how to hide `*.license` files in commonly used IDEs: +- **Visual Studio Code**: Add `**/*.license` to the +[`files.exclude` setting](https://code.visualstudio.com/docs/getstarted/userinterface#_explorer) +- **WebStorm**: Add `*.license` to the +[excluded files setting](https://www.jetbrains.com/help/webstorm/configuring-project-structure.html#exclude-by-pattern) diff --git a/apps/docs/versioned_docs/version-0.1.0/dev/02-dev-processes/03-licensing-and-copyright.md.license b/apps/docs/versioned_docs/version-0.1.0/dev/02-dev-processes/03-licensing-and-copyright.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/dev/02-dev-processes/03-licensing-and-copyright.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.1.0/dev/02-dev-processes/04-release-jayvee-version.md b/apps/docs/versioned_docs/version-0.1.0/dev/02-dev-processes/04-release-jayvee-version.md new file mode 100644 index 00000000..51630050 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/dev/02-dev-processes/04-release-jayvee-version.md @@ -0,0 +1,21 @@ +--- +title: Releasing a new Jayvee version +sidebar_position: 4 +--- + +## Version Numbers + +In this early stage of the project we do not yet follow [semantic versioning](https://semver.org/) since we expect the introduction of breaking changes frequently. +To indicate that, we only release alpha versions where the `version` is incremented with every release. +- For the npm packages, we use the version `0.0.<version>`. +- For the GitHub releases, we use the git tag `v0.0.<version>-alpha`. + +## Jayvee Release Procedure + +For releasing a new version of Jayvee, you need to complete the following steps: + +1. Increment the version in the `package.json` file. +2. Run `npx nx run docs:version-snapshot`. +3. If you are on a feature or dev branch, merge into main. +4. Create a GitHub release on the main branch. Attach a changelog. +5. The CI/CD will deal with the rest. diff --git a/apps/docs/versioned_docs/version-0.1.0/dev/02-dev-processes/04-release-jayvee-version.md.license b/apps/docs/versioned_docs/version-0.1.0/dev/02-dev-processes/04-release-jayvee-version.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/dev/02-dev-processes/04-release-jayvee-version.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.1.0/dev/02-dev-processes/_category_.json b/apps/docs/versioned_docs/version-0.1.0/dev/02-dev-processes/_category_.json new file mode 100644 index 00000000..c8f51245 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/dev/02-dev-processes/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Development Process", + "position": 2, + "link": { + "type": "generated-index", + "description": "Here you can find general processes around developing Jayvee." + } +} diff --git a/apps/docs/versioned_docs/version-0.1.0/dev/02-dev-processes/_category_.json.license b/apps/docs/versioned_docs/version-0.1.0/dev/02-dev-processes/_category_.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/dev/02-dev-processes/_category_.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.1.0/dev/03-architecture-overview.md b/apps/docs/versioned_docs/version-0.1.0/dev/03-architecture-overview.md new file mode 100644 index 00000000..cb4576fa --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/dev/03-architecture-overview.md @@ -0,0 +1,43 @@ +--- +title: Architecture overview +sidebar_position: 4 +--- + +Jayvee has a clear separation between the language itself and its execution. +This guide gives an overview of the overall architecture. + +## Language Server + +On the pure language side, the central project is the [language server](https://github.com/jvalue/jayvee/tree/main/libs/language-server) which is heavily built upon the [Langium framework](https://langium.org/). +It contains the syntax definition (i.e. the grammar) and is capable of performing static semantic analysis on models, so invalid models can be rejected and errors are reported to the user. +It uses the [Language Server Protocol](https://microsoft.github.io/language-server-protocol/) (LSP) for communicating with IDEs in order to provide common features such as diagnostics, auto completion and much more. + +## Interpreter + +The Jayvee [interpreter](https://github.com/jvalue/jayvee/tree/main/apps/interpreter) on the other hand is capable of running Jayvee models. +Therefore, it uses the language server as a library to perform lexing, parsing and semantic analysis on given models. +In case no errors were found during these phases, it executes the model by interpreting it. +This means that models are not compiled to any other language, like a compiler does, but rather directly executed on the fly without any code generation involved. +The interpreter comes with a command-line interface (CLI) for users, so they are able to execute Jayvee models easily. + +The following diagram visualizes the architecture described so far: + +```mermaid +graph LR +model[Jayvee Model] --> lexical(Lexical Analysis) +subgraph Jayvee Interpreter + subgraph Jayvee Language Server + subgraph Langium Framework + lexical --> syntax(Syntax Analysis) + end + syntax --> semantic(Semantic Analysis) + end + semantic --> interpretation(Interpretation) +end +``` + +## Jayvee Extensions + +Lastly, there are Jayvee extensions for adding additional features to Jayvee. +See [here](./04-guides/06-jayvee-extensions.md) for more details. +It is worth pointing out that extensions also follow the separation between language and execution described above. diff --git a/apps/docs/versioned_docs/version-0.1.0/dev/03-architecture-overview.md.license b/apps/docs/versioned_docs/version-0.1.0/dev/03-architecture-overview.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/dev/03-architecture-overview.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.1.0/dev/04-guides/01-jayvee-grammar.md b/apps/docs/versioned_docs/version-0.1.0/dev/04-guides/01-jayvee-grammar.md new file mode 100644 index 00000000..356fb09d --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/dev/04-guides/01-jayvee-grammar.md @@ -0,0 +1,34 @@ +--- +title: The Jayvee grammar +sidebar_position: 1 +--- + +The grammar of Jayvee describes the syntax and structure of the language. +It is located in the language server project. +The grammar itself is written in [Langium's own grammar language](https://langium.org/docs/grammar-language/) which is similar to [Xtext](https://www.eclipse.org/Xtext/) grammars. +Such grammar files are easily identifiable via their `.langium` file extension. + +The grammar is used to generate TypeScript interfaces that represent the Abstract Syntax Tree (AST) and different, semantically equivalent files to define syntax highlighting for different applications. +For instance, a [TextMate](https://macromates.com/manual/en/language_grammars) file is generated for the syntax highlighting in the Jayvee VS Code extension whereas a [Monarch](https://microsoft.github.io/monaco-editor/monarch.html) file is generated for the syntax highlighting in the Monaco editor. + +For further information on the grammar language of Langium, visit the corresponding [Langium documentation](https://langium.org/docs/grammar-language/). + +## Working with the grammar + +To run the code generation, either use `npm run generate` for solely the code generation or `npm run build` for an entire build. The code generation also generates further code, like the standard library. + +Whenever the grammar is changed and the code generation is run during development, it is advisory to **close and reopen the IDE**, so the changes are noticed and the file indexing is updated. + +## How to rename AST nodes + +Renaming AST nodes is not as straight forward as one might initially assume. +When renaming individual rules in the grammar, just the generated TypeScript code in `ast.ts` will reflect the renaming, but not the rest of the codebase. + +The following steps explain how to rename an AST node, so the change is reflected in the entire codebase. As an example, an AST node called `A` is supposed to be renamed to `B`: + +- Open the `ast.ts` file in the language server project +- Locate the `A` interface / type and the `isA` typeguard +- Use the rename feature by the IDE to perform the renaming from `A` to `B` and from `isA` to `isB` +- Open the grammar and locate the `A` rule +- Use the rename feature by the Langium VS Code extension to perform the renaming from `A` to `B` +- Run `npm run generate` diff --git a/apps/docs/versioned_docs/version-0.1.0/dev/04-guides/01-jayvee-grammar.md.license b/apps/docs/versioned_docs/version-0.1.0/dev/04-guides/01-jayvee-grammar.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/dev/04-guides/01-jayvee-grammar.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.1.0/dev/04-guides/02-working-with-the-ast.md b/apps/docs/versioned_docs/version-0.1.0/dev/04-guides/02-working-with-the-ast.md new file mode 100644 index 00000000..ca02dc33 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/dev/04-guides/02-working-with-the-ast.md @@ -0,0 +1,148 @@ +--- +title: Working with the AST +sidebar_position: 2 +--- + +The nodes of the Abstract Syntax Tree (AST) consist of types and interfaces generated from the language grammar. +See [here](./01-jayvee-grammar.md) for more information on that topic. + +The following sections provide practical guides and tips for working with nodes of the AST. + +## Dealing with potentially incomplete AST nodes + +According to the generated interfaces, properties of AST nodes are never `undefined`. +In practice however, this is not always the case. + +For example, consider the language server being confronted with an incomplete Jayvee model with syntax errors. +In such cases, properties of AST nodes are in fact `undefined`, despite their interface definition. +In the interpreter however, after lexical and syntactic analysis succeeded, it can be assumed that all AST nodes are complete (i.e. they have no undefined properties) and even that all references are resolved. + +In order to avoid accessing properties of AST nodes that are potentially undefined, it is recommended to access them via the [`?.` operator](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#optional-chaining) rather than the regular `.` operator. +Note that this might result in a conflict with the ESLint rule [`@typescript-eslint/no-unnecessary-condition`](https://typescript-eslint.io/rules/no-unnecessary-condition/), so it has to be disabled manually for such cases. + +For disabling the rule in an entire file, place this comment below the copyright and licensing header: + +```typescript +/* eslint-disable @typescript-eslint/no-unnecessary-condition */ +``` + +For just a single line, place this comment above that particular line: + +```typescript +// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition +``` + +<details> + +<summary>Full example</summary> + +Consider an exemplary AST node `A` with a property `x` of type `string`. To access that property safely: + +```typescript +import { A } from './ast' + +const astNode: A; + +// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition +const property: string | undefined = astNode?.x; +``` + +</details> + +## Usage of `assertUnreachable` + +Most times, it is beneficial to make case distinctions exhaustive, especially when working with AST nodes, properties with a [union literal type](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types) or enums. +Exhaustiveness in this context means, that the TypeScript compiler is supposed to yield an error if a case distinction does not cover all possibilities. + +Langium offers a function called `assertUnreachable` which is capable of enforcing exhaustiveness and producing compiler errors in case of violations. See the following examples to get an idea on how to use it in practice: + +<details> + +<summary>Example for an exhaustive switch statement on a union literal type</summary> + +```typescript +import { assertUnreachable } from 'langium'; + +const operator: '+' | '-'; + +switch(operator) { + case '+': { + // ... + break; + } + case '-': { + // ... + break; + } + default: { + // To ensure the switch being exhaustive on `operator`: + assertUnreachable(operator); + } +} +``` + +</details> + +<details> + +<summary>Example for an exhaustive if-elseif-else cascade using typeguards</summary> + +Consider the exemplary AST nodes `A`, `B` and `C` and that `A = B | C`: + +```typescript +import { assertUnreachable } from 'langium'; +import { A, B, isB, C, isC } from './ast' + +const astNode: A; + +if (isB(astNode)) { + // `astNode` has type `B` here +} else if (isC(astNode)) { + // `astNode` has type `C` here +} else { + // To ensure the if-elseif-else cascade being exhaustive on `astNode`: + assertUnreachable(astNode); +} +``` + +</details> + +## Usage of `assert` for expressing runtime expectations + +During development, it may occur that a certain condition is expected to **be always true** at runtime. +For example, an AST node being of a certain type or a property being defined. +The type system of TypeScript is not always able to infer such facts, so developers may have to express these expectations explicitly in their code. +Examples are [type assertions](https://www.typescriptlang.org/docs/handbook/advanced-types.html) or the [non-null assertion operator](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html#non-null-assertion-operator). +Their usage may be problematic in case the condition does not always hold, e.g. due to a bug in the program or a wrong expectation by the programmer. +In such cases, it is hard to locate the origin and debug the program because such operations are erased in the compiled JavaScript code. + +To avoid these issues, it is recommended to express such expectations as boolean expressions that are actually validated at runtime. +This can be easily achieved by using the `assert` function. +It evaluates a given boolean expression and throws an `AssertionError` in case it evaluates to `false`. +After calling `assert`, the type system of TypeScript assumes the condition to be `true` and afterwards narrows types accordingly. + +Here is an example of how to use it in practice: + +```typescript +// Import the `assert` function like this: +import { strict as assert } from 'assert'; + +import { A, B, isB } from './ast'; + +const astNode: A; +assert(isB(astNode)); +// Here `astNode` has type `B` + +const referenced = astNode?.reference?.ref; +assert(referenced !== undefined); +// Here `referred` is not `undefined` +``` + +## AST wrapper classes + +The generated interfaces for AST nodes in `ast.ts` are only meant to represent the AST structurally, they don't define any behavior. +Also, in case of syntactic sugar, there may be different kinds of AST nodes representing the same semantic language concept (e.g. single pipes with a verbose syntax or chained pipes). + +To cope with this problem, there is the concept of an `AstNodeWrapper`. +An AST node wrapper is capable of wrapping AST nodes that represent the same semantic language concept and adding behavior to them via custom methods. +To get an impression on how this can be done in practice, please have a look at the [`PipeWrapper` class](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/lib/ast/wrappers/pipe-wrapper.ts) and the [`AstNodeWrapper` interface](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/lib/ast/wrappers/ast-node-wrapper.ts). diff --git a/apps/docs/versioned_docs/version-0.1.0/dev/04-guides/02-working-with-the-ast.md.license b/apps/docs/versioned_docs/version-0.1.0/dev/04-guides/02-working-with-the-ast.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/dev/04-guides/02-working-with-the-ast.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.1.0/dev/04-guides/03-validation-and-diagnostics.md b/apps/docs/versioned_docs/version-0.1.0/dev/04-guides/03-validation-and-diagnostics.md new file mode 100644 index 00000000..328d5728 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/dev/04-guides/03-validation-and-diagnostics.md @@ -0,0 +1,64 @@ +--- +title: Validation and diagnostics +sidebar_position: 3 +--- + +Validation in the context of Jayvee can be understood as semantic analysis of Jayvee models. +The purpose is to uncover errors in models besides lexing, parsing and linking errors that the Langium framework already detects out of the box. +In case such errors are found, the language server makes the IDE display a diagnostic. +This means that the location of the error is underlined and an error message is shown. + +For example, each pipeline is expected to contain at least one starting block that requires no input. +Therefore, the language server analyzes every pipeline in a Jayvee model and checks for the presence of such a starting block. +In case no such block is found, the IDE underlines the pipeline definition in a red color and displays an error message. + +## How to implement validation + +### For arbitrary AST nodes + +In the file [`validation-registry.ts`](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/lib/validation/validation-registry.ts) of the language server, there is a registry containing validation functions for various AST nodes. +A validation function provided for a certain kind of AST node is called for every occurrence of such a node in a Jayvee model. +E.g. when registering a validation function for `PipelineDefinition` nodes, that function gets called once for each pipeline. + +A validation function always operates on a single, concrete AST node. +Besides the AST node, a `ValidationContext` object is passed to the function for reporting diagnostics via its `accept` method. + +The overall validation for a specific kind of AST node is usually subdivided into smaller checks that are called sequentially. +This potentially allows aborting the validation early, i.e. before all checks were run. +Aborting early may be useful if errors were reported during previous checks. +This can be determined via the `ValidationContext` which keeps track whether any errors occurred so far. + +A practical example where this plays a role are blocks: +When the type of a block is unknown to the language server, it makes no sense to validate inputs / outputs and properties of that block. +So, in case an error was reported during the check of the block type, the validation of that block is discontinued at that point. + +### For properties of blocks and constraints + +Validating property values of blocks / constraints works a bit differently because their individual validation is already incorporated into the overall validation framework. + +In general, the validation logic for a property values is located in the meta information of the block type / constraint type. +There, properties are defined using the `PropertySpecification` interface. +In order to add validation logic to a certain property, a validation function needs to be added to that property. + +For more complex validations that need to take multiple property values into account, it is also possible to provide a more general validation function which operates on the entire `PropertyBody` AST node. + +See [`text-range-selector-meta-inf.ts`](https://github.com/jvalue/jayvee/blob/main/libs/extensions/std/lang/src/text-range-selector-meta-inf.ts) for an example which includes both cases described above. + +## Reporting diagnostics + +During validation, diagnostics can be reported by calling the `accept` method of the given `ValidationContext` object. + +The call requires the severity of the diagnostic (`error`, `warning`, `info` or `hint`), the error message and its location. +The location is described by a `DiagnosticInfo` object which contains the affected AST node and optionally some further restrictions. +For more details, have a look at the [`DiagnosticInfo` interface](https://github.com/langium/langium/blob/main/packages/langium/src/validation/validation-registry.ts) by Langium. + +## Common issues + +When working on validations, a diagnostic with the following message may unexpectedly occur: `An error occurred during validation: ...` + +Such a diagnostic is generated by the Langium framework if an error object is thrown within the validation. +In most cases, the reason is the access of an `undefined` AST node property or an `AssertionError`. +For more details on such errors and how to avoid them, have a look at the documentation on [how to work with the AST](./02-working-with-the-ast.md). + +One way locate the origin of such errors is to debug the Jayvee VS Code extension, see [here](../02-dev-processes/02-debug-vs-code-extension.md) for more details. +Another possibility is to debug the interpreter and set appropriate breakpoints in the language server codebase. diff --git a/apps/docs/versioned_docs/version-0.1.0/dev/04-guides/03-validation-and-diagnostics.md.license b/apps/docs/versioned_docs/version-0.1.0/dev/04-guides/03-validation-and-diagnostics.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/dev/04-guides/03-validation-and-diagnostics.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.1.0/dev/04-guides/04-expressions-and-operators.md b/apps/docs/versioned_docs/version-0.1.0/dev/04-guides/04-expressions-and-operators.md new file mode 100644 index 00000000..91b51bd1 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/dev/04-guides/04-expressions-and-operators.md @@ -0,0 +1,151 @@ +--- +title: Expressions and operators +sidebar_position: 4 +--- + +Jayvee supports arbitrarily nested expressions and offers a variety of different operators. +Such expressions are similar to those used in programming languages, and they are intended to be read and understood intuitively. + +The following sections explain the definition of expressions in the language grammar, their type inference mechanism, and how the evaluation of expressions works. +As a small practical example, you may also have a look at the [arithmetics example by Langium](https://github.com/langium/langium/tree/main/examples/arithmetics) which has a heavy focus on mathematical expressions and functions. + +## Expressions in the grammar + +Expressions in the Jayvee grammar are defined in [`expression.langium`](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/grammar/expression.langium) and consist of operators (unary / binary) and literals. +Unary operators only have a single operand (e.g. the `not` operator) whereas binary operators require two operands (e.g. the `*` operator). + +The grammar is written in a way that literals end up in the leaves of the resulting AST and the nodes above represent the operators. + +As an example, have a look at the following AST structure of the expression `(-3 + 5) * 7`: + +```mermaid +classDiagram + +class `:BinaryOperator` { + operator = '*' +} + +%% Uses spaces in the name so it is unique +class ` :BinaryOperator` { + operator = '+' +} + +class `:UnaryOperator` { + operator = '-' +} + +%% Uses spaces in the name so it is unique +class ` :NumericLiteral` { + value = 3 +} + +%% Uses spaces in the name so it is unique +class ` :NumericLiteral` { + value = 5 +} + +class `:NumericLiteral` { + value = 7 +} + +`:BinaryOperator` --> ` :BinaryOperator` : leftOperand +`:BinaryOperator` --> `:NumericLiteral` : rightOperand +` :BinaryOperator` --> `:UnaryOperator` : leftOperand +`:UnaryOperator` --> ` :NumericLiteral` : operand +` :BinaryOperator` --> ` :NumericLiteral` : rightOperand +``` + +The AST is constructed in a way that ensures an unambiguous evaluation order when using depth-first search. +This implies that the AST structure already reflects the precedence and associativity of the operators, and that parentheses do not need to be explicitly represented. + +For more details on how such a grammar for expressions can be realized, see the [official Langium documentation](https://langium.org/docs/grammar-language/#tree-rewriting-actions), [this blog post](https://www.typefox.io/blog/parsing-expressions-with-xtext) and the following two sections which discuss the precedence and associativity of operators. + +### Precedence of operators + +The grammar implicitly defines the precedence of operators. +The operator precedence defines the order in which operators are evaluated within an expression. +For example, multiplication has a higher precedence than addition, which leads to multiplications being performed before additions. +In order to manually override such precedence conventions, parentheses can be used in expressions. + +The diagram below shows a more extensive precedence hierarchy, focused on common arithmetic operators. +Note that the hierarchy is arranged in ascending order, according to the operator precedence. +Such an order is similar to how operators in the actual Jayvee grammar are arranged: + +```mermaid +graph TD + subgraph BinaryOperators + add/sub(Addition Operator / Subtraction operator) --> mul/div(Multiplication Operator / Division Operator) + mul/div --> exp/root(Exponential Operator / Root Operator) + end + subgraph UnaryOperators + exp/root --> plus/minus(Plus Operator / Minus Operator) + end + plus/minus --> literal(Numeric Literal) +``` + +In the Jayvee grammar, every level of precedence is represented by a single grammar rule. +Within each such rule, the grammar rule which defines the operators with the next higher precedence is referenced. +E.g. the `AdditiveExpression` rule refers to the `MultiplicativeExpression` rule because multiplicative operators are supposed to have the next higher precedence. + +In order to alter the precedence of operators, their hierarchy of grammar rules has to be adjusted accordingly in [`expression.langium`](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/grammar/expression.langium). + +### Associativity of operators + +The associativity of operators defines the order in which operators with the same precedence are evaluated when they appear in succession without parentheses. +Operators may be either **left-associative**, **right-associative** or **non-associative**. +For example, depending on the associativity of the binary `+` operator, the expression `a + b + c` has different semantics: + +- Left-associative: `(a + b) + c` (evaluation from left to right) +- Right-associative: `a + (b + c)` (evaluation from right to left) +- Non-associative: _syntax error_, the operator cannot be chained + +The associativity of operators is also defined in the Jayvee grammar, more specifically by the structure of the grammar rules that define operators. +For details on how to encode the different kinds of associativity in grammar rules, have a look at the "Associativity" section near the end of [this blog post](https://www.typefox.io/blog/parsing-expressions-with-xtext). +The patterns described in the linked article can be found in [`expression.langium`](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/grammar/expression.langium), so each operator grammar rule encodes its own associativity. + +## Typing of expressions + +Jayvee has a mechanism for inferring and validating the type of a given expression. +This is achieved using a recursive algorithm which performs depth-first search on a given expression: + +The base case for the algorithm are literals, i.e. the tree leaves of an expression. +Depending on the type of literal, their type can be inferred trivially (in case of value literals) or otherwise from the context where the expression is located. + +For operators, the types of their operands are first inferred via recursion, and then it is checked whether they are supported by the operator. +Next, given the operand types, the resulting type is computed. +Such behavior is defined in a _type computer class_ located [here](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/lib/ast/expressions/type-computers). +Additionally, in [`operator-registry.ts`](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/lib/ast/expressions/operator-registry.ts), a type computer is registered for each kind of operator. + +In case the algorithm fails to infer a type, e.g. due to unsupported operand types or unresolved references in literals, the resulting type is `undefined`. +In order to report diagnostics in such cases, a `ValidationContext` object can be supplied when calling the type inference. + +## Evaluation of expressions + +The evaluation has the purpose of computing a single value out of an expression. +For a successful evaluation, it is a **precondition** that **a type could successfully be inferred** from the respective expression. +Also, the **value for each free variable** in that expression (i.e. literals resembling placeholders for values) needs to be **known beforehand**. +Therefore, an `ExecutionContext` object is passed to the evaluation which holds values for free variables in the current context. + +### Algorithm + +The algorithm for evaluating expressions is also based on a recursive depth-first search, similar to how the type inference works: + +Again, literals are the base case. A value literal trivially evaluates to its own value. +Other literals that resemble free variables evaluate to their value provided by the given `ExecutionContext` object. + +Regarding operators, their operands first are evaluated recursively. +Next, using the concrete operand values, the operator computes its resulting value. +This is defined in operator evaluator classes located [here](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/lib/ast/expressions/evaluators). +The classes are registered in [operator-registry.ts](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/lib/ast/expressions/operator-registry.ts) for every operator, similar to the previously mentioned type computers. + +The result of an evaluation may be `undefined` in case any errors occurred. +If the preconditions were all met, such errors are most likely arithmetic errors (like division by zero). +It is possible to provide a `ValidationContext` object to the evaluation for reporting such errors as diagnostics. + +### Evaluation strategies + +There are different evaluation strategies to choose from. +They affect the way, expressions are evaluated and thus have an impact on their semantics: + +- `exhaustive`: A full depth-first search is performed, all parts of an expression are evaluated. +- `lazy`: An evaluation with the least effort is performed. Logical operators use [short circuit semantics](https://en.wikipedia.org/wiki/Short-circuit_evaluation) and binary operators don't evaluate their right operand if the left one evaluated to `undefined`. diff --git a/apps/docs/versioned_docs/version-0.1.0/dev/04-guides/04-expressions-and-operators.md.license b/apps/docs/versioned_docs/version-0.1.0/dev/04-guides/04-expressions-and-operators.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/dev/04-guides/04-expressions-and-operators.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.1.0/dev/04-guides/05-standard-library.md b/apps/docs/versioned_docs/version-0.1.0/dev/04-guides/05-standard-library.md new file mode 100644 index 00000000..892e7ebe --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/dev/04-guides/05-standard-library.md @@ -0,0 +1,43 @@ +--- +title: Working with the Standard Library +sidebar_position: 5 +--- + +Jayvee ships with its own standard library on board, including the most often used valuetypes, transformations, and so on. +The standard library itself is written in `.jv` files [here](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/stdlib/). + +## Standard Library Contents + +The following elements are part of the standard library: + +## Builtin Contents +The implementations of builtin contents are not expressed in Jayvee itself but on the TypeScript layer. Examples: +- **Builtin valuetypes**: These valuetypes are the base for defining user-defined valuetypes in Jayvee, e.g., `text`, `integer`, `decimal`, `boolean`. +- **Builtin iotypes**: These iotypes are used to describe in inputs and outputs of blocktypes, e.g., `Sheet`, `File`. +- **Builtin blocktypes**: These blocktypes are the very basic building blocks in Jayvee, e.g., `HttpExtractor`, `SqliteLoader`. + +Builtin definitions are usually generated and added to the standard library from the internal representations of the concepts. + +### User-defined Contents +The implementations of user-defined contents are expressed in Jayvee itself. Examples: +- **User-defined valuetypes**: These valuetypes are based on builtin or other user-defined valuetypes. Their definition is expressed natively in Jayvee, e.g., `Percent`. +- **User-defined blocktypes**: These blocktypes are based on builtin or other user-defined blocktypes. Their definition is expressed natively in Jayvee. + +We use `jv` files to add user-defined valuetypes to the standard library (see below). + + +## Extending the Standard Library + +Just add `jv` files to the directory [here](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/stdlib/). It is crawled hierarchically, meaning that you can also organize files in folders. + +## Implementation + +### 1. Code generation + +We use code generation to transform these `.jv` files into TypeScript files that the language server can used. The [generation script](https://github.com/jvalue/jayvee/tree/main/tools/scripts/language-server/generate-stdlib.mjs) is run via `npm run generate` next to the AST generation. + +### 2. Builtin libraries + +The solution we chose to implement the standard library mechanism is close to the [builtin library tutorial](https://langium.org/guides/builtin-library/) by Langium. The following components are of interest: +- [JayveeWorkspaceManager](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/lib/builtin-library/jayvee-workspace-manager.ts) in the `language-server` that registers all libraries with the langium framework. +- [StandardLibraryFileSystemProvider](https://github.com/jvalue/jayvee/tree/main/apps/vs-code-extension/src/standard-library-file-system-provider.ts) in the `vs-code-extension` that registers all libraries with the vscode plugin framework. \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.1.0/dev/04-guides/05-standard-library.md.license b/apps/docs/versioned_docs/version-0.1.0/dev/04-guides/05-standard-library.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/dev/04-guides/05-standard-library.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.1.0/dev/04-guides/06-jayvee-extensions.md b/apps/docs/versioned_docs/version-0.1.0/dev/04-guides/06-jayvee-extensions.md new file mode 100644 index 00000000..11c34e02 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/dev/04-guides/06-jayvee-extensions.md @@ -0,0 +1,280 @@ +--- +title: Jayvee Extensions +sidebar_position: 6 +--- + +## Concepts + +### Jayvee extension + +A Jayvee extension is used to add new block types to the language without having to modify the actual grammar of the language. Such a Jayvee extension usually consists of two parts: a language extension and an execution extension which are described in the upcoming two sections. + +Jayvee extensions that shall be used by default are bundled into the so-called [standard extension](https://github.com/jvalue/jayvee/tree/main/libs/extensions/std). That way, the [language server](https://github.com/jvalue/jayvee/tree/main/libs/language-server) and the [interpreter](https://github.com/jvalue/jayvee/tree/main/apps/interpreter) are able to load them out of the box. + +### Language extension + +A language extension defines meta information of block types which are required by the +[language server](https://github.com/jvalue/jayvee/tree/main/libs/language-server). +Such meta information describes properties of +block types such as their names, input / output types and their properties. + +Note that language extensions don't define any behavior. Instead, this is done by the corresponding execution extension. + +### Execution extension + +An execution extension defines behavior for block types. They build on the meta information from the corresponding +language extension, e.g. input / output types of the block need to match the signature of the execution method and +properties are accessed by their specified name. + +Execution extensions are only required by the [interpreter](https://github.com/jvalue/jayvee/tree/main/apps/interpreter) and not necessarily by the [language server](https://github.com/jvalue/jayvee/tree/main/libs/language-server) as they solely define behavior. + +## Recipes + +### Add a new Jayvee extension + +#### 1. Generate language and execution libraries + +```bash +npx nx g @nrwl/node:library --name="extensions/<extension-name>/lang" +npx nx g @nrwl/node:library --name="extensions/<extension-name>/exec" +``` + +#### 2. Create extension classes + +In `libs/extensions/<extension-name>/lang/src/extension.ts`: + +```typescript +import { + BlockMetaInformation, + ConstructorClass, + JayveeLangExtension, +} from '@jvalue/jayvee-language-server'; + +export class MyLangExtension implements JayveeLangExtension { + getBlockMetaInf(): Array<ConstructorClass<BlockMetaInformation>> { + return []; + } +} + +``` + +In `libs/extensions/<extension-name>/exec/src/extension.ts`: + +```typescript +import { BlockExecutorClass, JayveeExecExtension } from '@jvalue/jayvee-execution'; + +export class MyExecExtension implements JayveeExecExtension { + getBlockExecutors(): BlockExecutorClass[] { + return []; + } +} +``` + +#### 3. Export extension classes + +In `libs/extensions/<extension-name>/lang/src/index.ts`: + +```typescript +export * from './extension'; +``` + +In `libs/extensions/<extension-name>/exec/src/index.ts`: + +```typescript +export * from './extension'; +``` + +#### 4. Register new extension classes in the standard extension + +In `libs/extensions/std/lang/src/extension.ts`: + +```typescript +// ... + +import { MyLangExtension } from '@jvalue/jayvee-extensions/<extension-name>/lang'; + +export class StdLangExtension implements JayveeLangExtension { + private readonly wrappedExtensions: JayveeLangExtension[] = [ + // ... + // Register your language extension here: + new MyLangExtension(), + // ... + ]; + + // ... +} +``` + +In `libs/extensions/std/exec/src/extension.ts`: + +```typescript +// ... + +import { MyExecExtension } from '@jvalue/jayvee-extensions/<extension-name>/exec'; + +export class StdExecExtension implements JayveeExecExtension { + private readonly wrappedExtensions: JayveeExecExtension[] = [ + // ... + // Register your execution extension here: + new MyExecExtension(), + // ... + ]; + + // ... +} +``` + +### Add a new block type in a Jayvee extension + +#### 1. Implement `BlockMetaInformation` + +The following example defines a block type called `MyExtractor`. This block type, for instance, takes no input and +outputs a sheet. Furthermore, it defines two properties: + +- `url` of type text +- `limit` of type integer and default value `10` + - Considered optional due to the specified default value + +In `libs/extensions/<extension-name>/lang/src/lib/my-extractor-meta-inf.ts`: + +```typescript +import { + BlockMetaInformation, + IOType, + PrimitiveValuetypes, +} from '@jvalue/jayvee-language-server'; + +export class MyExtractorMetaInformation extends BlockMetaInformation { + constructor() { + super( + // How the block type should be called: + 'MyExtractor', + + // Property definitions: + { + url: { + type: PrimitiveValuetypes.Text, + }, + limit: { + type: PrimitiveValuetypes.Integer, + defaultValue: 10, + }, + }, + + // Input type: + IOType.NONE, + + // Output type: + IOType.SHEET, + ); + } +} +``` + +> **Note** +> Use `IOType.NONE` whenever you expect no input or output: +> +> - Use it as input type if you want to define an extractor +> - Use it as output type if you want to define a loader + +#### 2. Register the new `BlockMetaInformation` in the language extension + +In `libs/extensions/<extension-name>/lang/src/extension.ts`: + +```typescript +// ... + +import { MyExtractorMetaInformation } from './lib/my-extractor-meta-inf'; + +export class MyLangExtension implements JayveeLangExtension { + getBlockMetaInf(): Array<ConstructorClass<BlockMetaInformation>> { + return [ + // ... + // Register your meta information here: + MyExtractorMetaInformation, + // ... + ]; + } +} +``` + +#### 3. Implement `BlockExecutor` + +The following example implements an executor for the previously defined block type `MyExtractor`. + +The `execute` method defines the behavior when a block is executed. Its signature matches the input and output types defined in `MyExtractorMetaInformation`. + +In `libs/extensions/<extension-name>/exec/src/lib/my-extractor-executor.ts`: + +```typescript +import * as R from '@jvalue/jayvee-execution'; +import { + AbstractBlockExecutor, + BlockExecutorClass, + ExecutionContext, + Sheet, + implementsStatic, +} from '@jvalue/jayvee-execution'; +import { IOType, PrimitiveValuetypes } from '@jvalue/jayvee-language-server'; + +@implementsStatic<BlockExecutorClass>() +export class MyExtractorExecutor + extends AbstractBlockExecutor<IOType.NONE, IOType.SHEET> +{ + // Needs to match the type in meta information: + public static readonly type = 'MyExtractor'; + + public readonly inputType = IOType.NONE; + public readonly outputType = IOType.SHEET; + + async doExecute( + input: None, + context: ExecutionContext, + ): Promise<R.Result<Sheet>> { + // Accessing property values by their name: + const url = context.getPropertyValue( + 'url', + PrimitiveValuetypes.Text, + ); + const limit = context.getPropertyValue( + 'limit', + PrimitiveValuetypes.Integer, + ); + + // ... + + if (error) { + return R.err(...); + } + + return R.ok(...); + } +} +``` + +> **Info** +> The interface `BlockExecutor<I,O>` is used as an API for block executors. The abstract class `AbstractBlockExecutor<I,O>` gives some further functionality for free, e.g., debug logging. + +> **Warning** +> The generic types of `AbstractBlockExecutor<I,O>` need to match the input and output types of the corresponding `BlockMetaInformation`. + +#### 4. Register the new `BlockExecutor` in the execution extension + +In `libs/extensions/<extension-name>/exec/src/extension.ts`: + +```typescript +// ... + +import { MyExtractorExecutor } from './lib/my-extractor-executor'; + +export class MyExecExtension implements JayveeExecExtension { + getBlockExecutors(): BlockExecutorClass[] { + return [ + // ... + // Register your block executor here: + MyExtractorExecutor, + // ... + ]; + } +} +``` diff --git a/apps/docs/versioned_docs/version-0.1.0/dev/04-guides/06-jayvee-extensions.md.license b/apps/docs/versioned_docs/version-0.1.0/dev/04-guides/06-jayvee-extensions.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/dev/04-guides/06-jayvee-extensions.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.1.0/dev/04-guides/_category_.json b/apps/docs/versioned_docs/version-0.1.0/dev/04-guides/_category_.json new file mode 100644 index 00000000..80f6fce8 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/dev/04-guides/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Guides", + "position": 4, + "link": { + "type": "generated-index", + "description": "Here you can find guides that will help you developing certain aspects of Jayvee." + } +} diff --git a/apps/docs/versioned_docs/version-0.1.0/dev/04-guides/_category_.json.license b/apps/docs/versioned_docs/version-0.1.0/dev/04-guides/_category_.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/dev/04-guides/_category_.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.1.0/dev/05-design-principles.md b/apps/docs/versioned_docs/version-0.1.0/dev/05-design-principles.md new file mode 100644 index 00000000..8b15dd8c --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/dev/05-design-principles.md @@ -0,0 +1,24 @@ +--- +title: Design Principles +sidebar_position: 5 +--- + +When deciding on new features for the domain-specific language itself, we try to adhere to the following high level guidelines. Of course, none of these statements is set in stone and every decision is a tradeoff. + +## Jayvee Manifesto +_Inspired by the [Agile Manifesto](https://agilemanifesto.org/)._ + +We are uncovering better ways of _modeling data pipelines by providing a domain-specific language for data engineering and making it easy for everyone to participate in it_. + +Through this work we have come to value: + +1. **Describing a goal state** over how to get there. +2. **Explicit modeling** over hidden magic. +3. **Composition** over inheritance. +4. **Flat structures** over deep nesting. + +That is, while there is value in the items on the right, we value the items on the left more. + +Through this work, we also have come to explore: + +5. **Libraries** over language features. \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.1.0/dev/05-design-principles.md.license b/apps/docs/versioned_docs/version-0.1.0/dev/05-design-principles.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/dev/05-design-principles.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.1.0/dev/12-jayvee-testing.md b/apps/docs/versioned_docs/version-0.1.0/dev/12-jayvee-testing.md new file mode 100644 index 00000000..7936a75f --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/dev/12-jayvee-testing.md @@ -0,0 +1,249 @@ +--- +title: Writing tests for Jayvee +--- + +In order to ensure that Jayvee works as intended and to catch breaking changes, we have implemented the following components for regression testing: +- Testing utils: utils to create Langium Typescript objects from *.jv test assets (see [here](#testing-utils)) as well as mocks for execution testing (see [here](#testing-utils-1)) +- [Grammar tests](#grammar-tests): test the grammar parsing and validation +- [Execution tests](#execution-tests): test the execution of blocks + +## Conventions +All of the existing tests follow these conventions: +1. The `<file-name>.spec.ts` file is located next to the `<file-name>.ts` file itself. +2. The `*.jv` assets are located inside a `test/assets/<file-name>` folder. +Take a look at one of the exisiting tests for more details. + +## Grammar tests +These kind of tests are mainly located inside the [language-server](https://github.com/jvalue/jayvee/tree/main/libs/language-server) as well as the language parts of each extension (for example [std/lang](https://github.com/jvalue/jayvee/tree/main/libs/extensions/std/lang)). + +### Testing utils +The testing utils are located inside the `language-server` in a dedicated [test folder](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/test). +These utils can be imported using `@jvalue/jayvee-language-server/test` and contain the following parts: + +[**langium-utils.ts**](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/test/langium-utils.ts): +This utils file contains two functions: +- `parseHelper` to simplify parsing the input (content of a *.jv file) and returning the corresponding `LangiumDocument`, and +- `validationHelper` parse and validate the created document. +They are kept in a separate file due to being copied from the Langium repository and thus subject to a different code license and copyright. + +[**utils.ts**](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/test/utils.ts): +This file contains custom testing utility utils functions, like `readJvTestAssetHelper` for reading jv test assets. +Example: +``` ts +import * as path from 'path'; + +import { createJayveeServices } from '@jvalue/jayvee-language-server'; +import { + ParseHelperOptions, + expectNoParserAndLexerErrors, + parseHelper, + readJvTestAssetHelper, +} from '@jvalue/jayvee-language-server/test'; +import { AstNode, LangiumDocument } from 'langium'; + +describe('My example test', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../test/assets/', // localized path to test assets folder + ); + + beforeAll(() => { + // [...] register extensions etc + const services = createJayveeServices(NodeFileSystem).Jayvee; // Or retrieve them if services already exist + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + // [...] + + it('My dummy test', () => { + const text = readJvTestAsset('<sub-folder>/<test-asset-name>.jv'); + + const document = await parse(text); + expectNoParserAndLexerErrors(document); + // Rest of test + }); +}); +``` +If you want to simply validate the test assets, simply replace `parseHelper` with `validationHelper` (and adjust the types). +You can find detailed documentation of all the utility functions directly in the code. + +[**extension/**](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/test/extension): +This folder contains a Jayvee extension for testing. +If there are certain blocks required for testing a certain feature, they can be defined here. +One such example is the already defined `TestProperty` block which has a multitude of different properties, each with a different type. +This block is used for testing properties and property-assignments. +The extension provides loader and extractor blocks for all IOTypes without any properties. +These blocks are automatically generated at runtime with the following naming scheme: +`Test${ioType}${io === 'input' ? 'Loader' : 'Extractor'}` (Example: `TestFileExtractor`). +This allows for easy (grammar) testing of non loader/extractor blocks: +``` jv +pipeline Pipeline { + + TestExtractor -> BlockUnderTest -> TestLoader; + + block BlockUnderTest oftype CellWriter { + at: range A1:A3; + write: ['values', 'to', 'write']; + } + + block TestExtractor oftype TestSheetExtractor { } + block TestLoader oftype TestSheetLoader { } +} +``` + +### Existing tests +Currently there are already tests for the following parts: +- Language-server validation checks (located [here](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/lib/validation)) +- Language-server constraint validation (located [here](https://github.com/jvalue/jayvee/tree/dev/libs/language-server/src/lib/constraint)) +- Custom block (property) validation of the three existing extensions (std extension located [here](https://github.com/jvalue/jayvee/blob/dev/libs/extensions/std/lang/src)) +- Grammar validation tests for all official full examples from the [/example](https://github.com/jvalue/jayvee/tree/main/example) folder (located [here](https://github.com/jvalue/jayvee/blob/dev/libs/extensions/std/lang/src/example-validation.spec.ts)) +- Grammar validation tests for all block examples of the std extension (located [here](https://github.com/jvalue/jayvee/blob/dev/libs/extensions/std/lang/src/meta-inf-example-validation.spec.ts)) + +## Execution tests +These kind of tests are mainly located inside the [interpreter](https://github.com/jvalue/jayvee/tree/main/libs/language-server), the [interpreter-lib](https://github.com/jvalue/jayvee/tree/dev/libs/interpreter-lib), the [execution lib](https://github.com/jvalue/jayvee/tree/dev/libs/execution) as well as the execution parts of each extension (for example [std/exec](https://github.com/jvalue/jayvee/tree/main/libs/extensions/std/exec)). + +### Testing utils +The testing utils for execution tests are spread between the extensions, with the interfaces and base utils located inside the [execution lib](https://github.com/jvalue/jayvee/tree/dev/libs/execution). +They can be imported using `@jvalue/jayvee-extensions/rdbms/test`, `@jvalue/jayvee-extensions/std/test` and `@jvalue/jayvee-execution/test`. + +[**utils.ts**](https://github.com/jvalue/jayvee/blob/dev/libs/execution/test/utils.ts): +At the moment this only contains two functions: +- `clearBlockExecutorRegistry` for clearing the registry containing all `BlockExecutor`s, and +- `clearConstraintExecutorRegistry` clearing the corresponding `ConstraintExecutor`s registry. +They are required in case the tested method initializes Jayvee itself (see [smoke test](#existing-tests-1)). + +[**block-executor-mocks.ts**](https://github.com/jvalue/jayvee/blob/dev/libs/execution/test/block-executor-mock.ts): +`BlockExecutorMock` interface for defining mocks for `AbstractBlockExecutor`. Generally only loader and executor blocks require mocks, because they interact with "the outside world" (i.e. `HttpExtractor` making http calls). +Due to how vastly different each `BlockExecutor` can be, this interface is very simple, containing only a `setup(...args: unknown[])` and a `restore()` method. See below for existing implementations. + +[**rdbms/exec/test**](https://github.com/jvalue/jayvee/tree/dev/libs/extensions/rdbms/exec/test): +Contains the implementation of `BlockExecutorMock` for `PostgresLoaderExecutor` and `SQLiteLoaderExecutor`. +Both of these executors are mocked using `jest.mock` to mock the corresponding libraries (`pg` and `sqlite3`) +**Usage:** +``` ts +import { + PostgresLoaderExecutorMock, + SQLiteLoaderExecutorMock, +} from '@jvalue/jayvee-extensions/rdbms/test'; + +// Global mocking of external library at the top of test file required, +// even though the mocking is encapsulated in helper classes +jest.mock('pg', () => { + const mClient = { + connect: jest.fn(), + query: jest.fn(), + end: jest.fn(), + }; + return { Client: jest.fn(() => mClient) }; +}); +jest.mock('sqlite3', () => { + const mockDB = { + close: jest.fn(), + run: jest.fn(), + }; + return { Database: jest.fn(() => mockDB) }; +}); + +describe('Dummy describe', () => { + // [...] + + let postgresLoaderMock: PostgresLoaderExecutorMock; + let sqliteLoaderMock: SQLiteLoaderExecutorMock; + + beforeAll(() => { + postgresLoaderMock = new PostgresLoaderExecutorMock(); + sqliteLoaderMock = new SQLiteLoaderExecutorMock(); + }); + + afterEach(() => { + postgresLoaderMock.restore(); + sqliteLoaderMock.restore(); + }); + + it('Dummy test', async () => { + // Prepare mocks + postgresLoaderMock.setup(); + sqliteLoaderMock.setup(); + + // [...] execute test + + expect(postgresLoaderMock.pgClient.connect).toBeCalledTimes(1); + expect(postgresLoaderMock.pgClient.query).toBeCalledTimes(1); + expect(postgresLoaderMock.pgClient.end).toBeCalledTimes(1); + expect(sqliteLoaderMock.sqliteClient.run).toBeCalledTimes(1); + expect(sqliteLoaderMock.sqliteClient.close).toBeCalledTimes(1); + }); +}); +``` + +[**std/exec/test/mocks**](https://github.com/jvalue/jayvee/tree/dev/libs/extensions/std/exec/test): +Contains the implementation of `BlockExecutorMock` for `HttpExtractorExecutorMock`. +This implementation uses [nock](https://www.npmjs.com/package/nock) for mocking HTTP(S) responses. +The `setup` method is further specified requiring one parameter `registerMocks: () => Array<nock.Scope>`, which returns all used `nock.Scope` (i.e. the return value of `nock('<URL>')`), see usage below: +**Usage:** +``` ts +import * as path from 'path'; + +import { HttpExtractorExecutorMock } from '@jvalue/jayvee-extensions/std/test'; + +describe('Dummy describe', () => { + // [...] + + let httpExtractorMock: HttpExtractorExecutorMock; + + beforeAll(() => { + httpExtractorMock = new HttpExtractorExecutorMock(); + }); + + afterEach(() => { + httpExtractorMock.restore(); + }); + + it('should have no errors when executing gtfs-static-and-rt.jv example', async () => { + // Prepare mocks + httpExtractorMock.setup(() => { + return [ + nock( + '<URL_1>', + ) + .get('<PATH>') + .replyWithFile( + 200, + path.resolve(__dirname, '../test/assets/file1.zip'), + { + 'Content-Type': 'application/octet-stream', + }, + ), + nock('<URL_2>') + .get('<PATH_1>') + .replyWithFile( + 200, + path.resolve( + __dirname, + '../test/assets/file2', + ), + { + 'Content-Type': 'application/octet-stream', + }, + ) + .get('<PATH_2>') + .reply(200, { content: "My dummy json reply." }), + ]; + }) + + // [...] execute test + + expect(httpExtractorMock.nockScopes.every((scope) => scope.isDone())); + }); +}); +``` + +### Existing tests +Currently there are already tests for the following parts: +- Smoke test for official examples (located [here](https://github.com/jvalue/jayvee/blob/dev/apps/interpreter/src/examples-smoke-test.spec.ts)) diff --git a/apps/docs/versioned_docs/version-0.1.0/dev/12-jayvee-testing.md.license b/apps/docs/versioned_docs/version-0.1.0/dev/12-jayvee-testing.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/dev/12-jayvee-testing.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.1.0/user/block-types/ArchiveInterpreter.md b/apps/docs/versioned_docs/version-0.1.0/user/block-types/ArchiveInterpreter.md new file mode 100644 index 00000000..66929ee2 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/block-types/ArchiveInterpreter.md @@ -0,0 +1,33 @@ +--- +title: ArchiveInterpreter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `File` + +Output type: `FileSystem` + +## Description + +Interprets a `File` as an archive file and converts it to a `FileSystem`. The archive file root is considered the root of the `FileSystem`. + +## Example 1 + +```jayvee +block ZipArchiveInterpreter oftype ArchiveInterpreter { + archiveType: "zip"; +} +``` + +Interprets a `File` as a ZIP-archive and creates a `FileSystem` of its extracted contents. + +## Properties + +### `archiveType` + +Type `text` + +#### Description + +The archive type to be interpreted, e.g., "zip" or "gz". diff --git a/apps/docs/versioned_docs/version-0.1.0/user/block-types/ArchiveInterpreter.md.license b/apps/docs/versioned_docs/version-0.1.0/user/block-types/ArchiveInterpreter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/block-types/ArchiveInterpreter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.1.0/user/block-types/CSVInterpreter.md b/apps/docs/versioned_docs/version-0.1.0/user/block-types/CSVInterpreter.md new file mode 100644 index 00000000..f4a09823 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/block-types/CSVInterpreter.md @@ -0,0 +1,63 @@ +--- +title: CSVInterpreter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `TextFile` + +Output type: `Sheet` + +## Description + +Interprets an input file as a csv-file containing string-values delimited by `delimiter` and outputs a `Sheet`. + +## Example 1 + +```jayvee +block AgencyCSVInterpreter oftype CSVInterpreter { + delimiter: ";"; + } +``` + +Interprets an input file as a csv-file containing string-values delimited by `;` and outputs `Sheet`. + +## Properties + +### `delimiter` + +Type `text` + +Default: `","` + +#### Description + +The delimiter for values in the CSV file. + +#### Example 1 + +```jayvee +delimiter: "," +``` + +Commas are used to separate values in the CSV file. + +### `enclosing` + +Type `text` + +Default: `""` + +#### Description + +The enclosing character that may be used for values in the CSV file. + +### `enclosingEscape` + +Type `text` + +Default: `""` + +#### Description + +The character to escape enclosing characters in values. diff --git a/apps/docs/versioned_docs/version-0.1.0/user/block-types/CSVInterpreter.md.license b/apps/docs/versioned_docs/version-0.1.0/user/block-types/CSVInterpreter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/block-types/CSVInterpreter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.1.0/user/block-types/CellRangeSelector.md b/apps/docs/versioned_docs/version-0.1.0/user/block-types/CellRangeSelector.md new file mode 100644 index 00000000..075fe4a9 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/block-types/CellRangeSelector.md @@ -0,0 +1,41 @@ +--- +title: CellRangeSelector +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Sheet` + +Output type: `Sheet` + +## Description + +Selects a subset of a `Sheet` to produce a new `Sheet`. + +## Example 1 + +```jayvee +block CarsCoreDataSelector oftype CellRangeSelector { + select: range A1:E*; +} +``` + +Selects the cells in the given range and produces a new `Sheet` containing only the selected cells. + +## Properties + +### `select` + +Type `CellRange` + +#### Description + +The cell range to select. + +#### Example 1 + +```jayvee +select: range A1:E* +``` + +Select cells from `A1` to the last cell of column `E`. diff --git a/apps/docs/versioned_docs/version-0.1.0/user/block-types/CellRangeSelector.md.license b/apps/docs/versioned_docs/version-0.1.0/user/block-types/CellRangeSelector.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/block-types/CellRangeSelector.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.1.0/user/block-types/CellWriter.md b/apps/docs/versioned_docs/version-0.1.0/user/block-types/CellWriter.md new file mode 100644 index 00000000..fd1f5d18 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/block-types/CellWriter.md @@ -0,0 +1,89 @@ +--- +title: CellWriter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Sheet` + +Output type: `Sheet` + +## Description + +Writes textual values into cells of a `Sheet`. The number of text values needs to match the number of cells to write into. + +## Example 1 + +```jayvee +block NameHeaderWriter oftype CellWriter { + at: cell A1; + write: ["Name"]; +} +``` + +Write the value "Name" into cell `A1`. + +## Example 2 + +```jayvee +block HeaderSequenceWriter oftype CellWriter { + at: range A1:A2; + write: ["Name", "Age"]; +} +``` + +Write the values "Name", "Age" into cells `A1` and `A2`. + +## Properties + +### `write` + +Type `Collection<text>` + +#### Description + +The values to write. + +#### Example 1 + +```jayvee +write: ["Name"] +``` + +Write the value "Name" into the cell. + +#### Example 2 + +```jayvee +write: ["Name1", "Name2"] +``` + +Write the value "Name1" into the first cell and "Name2 into the second. + +### `at` + +Type `CellRange` + +#### Description + +The cells to write into. + +#### Validation + +Needs to be a one-dimensional range of cells. + +#### Example 1 + +```jayvee +at: cell A1 +``` + +Write into cell A1. + +#### Example 2 + +```jayvee +at: range A1:A3 +``` + +Write into cells A1, A2 and A3. diff --git a/apps/docs/versioned_docs/version-0.1.0/user/block-types/CellWriter.md.license b/apps/docs/versioned_docs/version-0.1.0/user/block-types/CellWriter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/block-types/CellWriter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.1.0/user/block-types/ColumnDeleter.md b/apps/docs/versioned_docs/version-0.1.0/user/block-types/ColumnDeleter.md new file mode 100644 index 00000000..d3b9c328 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/block-types/ColumnDeleter.md @@ -0,0 +1,53 @@ +--- +title: ColumnDeleter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Sheet` + +Output type: `Sheet` + +## Description + +Deletes columns from a `Sheet`. Column IDs of subsequent columns will be shifted accordingly, so there will be no gaps. + +## Example 1 + +```jayvee +block MpgColumnDeleter oftype ColumnDeleter { + delete: [column B]; +} +``` + +Deletes column B (i.e. the second column). + +## Properties + +### `delete` + +Type `Collection<CellRange>` + +#### Description + +The columns to delete. + +#### Validation + +You need to specify at least one column. + +#### Example 1 + +```jayvee +delete: [column B] +``` + +Delete column B. + +#### Example 2 + +```jayvee +delete: [column B, column C] +``` + +Delete column B and column C. diff --git a/apps/docs/versioned_docs/version-0.1.0/user/block-types/ColumnDeleter.md.license b/apps/docs/versioned_docs/version-0.1.0/user/block-types/ColumnDeleter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/block-types/ColumnDeleter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.1.0/user/block-types/FilePicker.md b/apps/docs/versioned_docs/version-0.1.0/user/block-types/FilePicker.md new file mode 100644 index 00000000..8f4a389c --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/block-types/FilePicker.md @@ -0,0 +1,33 @@ +--- +title: FilePicker +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `FileSystem` + +Output type: `File` + +## Description + +Selects one `File` from a `FileSystem` based on its relative path to the root of the `FileSystem`. If no file matches the relative path, no output is created and the execution of the pipeline is aborted. + +## Example 1 + +```jayvee +block AgencyFilePicker oftype FilePicker { + path: "./agency.txt"; +} +``` + +Tries to pick the file `agency.txt` from the root of the provided `FileSystem`. If `agency.txt` exists it is passed on as `File`, if it does not exist the execution of the pipeline is aborted. + +## Properties + +### `path` + +Type `text` + +#### Description + +The path of the file to select, relative to the root of the provided `FileSystem`. diff --git a/apps/docs/versioned_docs/version-0.1.0/user/block-types/FilePicker.md.license b/apps/docs/versioned_docs/version-0.1.0/user/block-types/FilePicker.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/block-types/FilePicker.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.1.0/user/block-types/GtfsRTInterpreter.md b/apps/docs/versioned_docs/version-0.1.0/user/block-types/GtfsRTInterpreter.md new file mode 100644 index 00000000..f92b27ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/block-types/GtfsRTInterpreter.md @@ -0,0 +1,81 @@ +--- +title: GtfsRTInterpreter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `File` + +Output type: `Sheet` + +## Description + +Interprets an protobuf file (binary) of type `File` by decoding the file according to `gtfs-realtime.proto`. Outputs the extracted entity defined by `entity` as a `Sheet` + +## Example 1 + +```jayvee +block GtfsRTTripUpdateInterpreter oftype GtfsRTInterpreter{ + entity: "trip_update"; +} +``` + +A file is interpretet as an GTFS-RT file, which contains TripUpdate. + +## Properties + +### `entity` + +Type `text` + +#### Description + +Entity to process from GTFS-RT-feed (`trip_update`, `alert` or `vehicle`). + We currently support following Output-Sheets, each are an equivalent to the flattened Element Index defined in <https://developers.google.com/transit/gtfs-realtime/reference#element-index> (just required fields are included)): + + Entity TripUpdate: + ``` + [ + 'header.gtfs_realtime_version', + 'header.timestamp', + 'header.incrementality', + 'entity.id', + 'entity.trip_update.trip.trip_id', + 'entity.trip_update.trip.route_id', + 'entity.trip_update.stop_time_update.stop_sequence', + 'entity.trip_update.stop_time_update.stop_id', + 'entity.trip_update.stop_time_update.arrival.time', + 'entity.trip_update.stop_time_update.departure.time', + ]; + + ``` + Entity VehiclePosition: + ``` + [ + 'header.gtfs_realtime_version', + 'header.timestamp', + 'header.incrementality', + 'entity.id', + 'entity.vehicle_position.vehicle_descriptor.id', + 'entity.vehicle_position.trip.trip_id', + 'entity.vehicle_position.trip.route_id', + 'entity.vehicle_position.position.latitude', + 'entity.vehicle_position.position.longitude', + 'entity.vehicle_position.timestamp', + ]; + ``` + + Entity Alert: + ``` + [ + 'header.gtfs_realtime_version', + 'header.timestamp', + 'header.incrementality', + 'entity.id', + 'entity.alert.informed_entity.route_id', + 'entity.alert.header_text', + 'entity.alert.description_text', + ]; + ``` + + diff --git a/apps/docs/versioned_docs/version-0.1.0/user/block-types/GtfsRTInterpreter.md.license b/apps/docs/versioned_docs/version-0.1.0/user/block-types/GtfsRTInterpreter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/block-types/GtfsRTInterpreter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.1.0/user/block-types/HttpExtractor.md b/apps/docs/versioned_docs/version-0.1.0/user/block-types/HttpExtractor.md new file mode 100644 index 00000000..dde1b124 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/block-types/HttpExtractor.md @@ -0,0 +1,122 @@ +--- +title: HttpExtractor +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `None` + +Output type: `File` + +## Description + +Extracts a `File` from the web. + +## Example 1 + +```jayvee +block CarsFileExtractor oftype HttpExtractor { + url: "tinyurl.com/4ub9spwz"; +} +``` + +Fetches a file from the given URL. + +## Properties + +### `url` + +Type `text` + +#### Description + +The URL to the file in the web to extract. + +#### Example 1 + +```jayvee +url: "tinyurl.com/4ub9spwz" +``` + +Specifies the URL to fetch the data from. + +### `retries` + +Type `integer` + +Default: `0` + +#### Description + +Configures how many retries should be executed after a failure fetching the data. + +#### Example 1 + +```jayvee +retries: 3 +``` + +Executes up to 3 retries if the original retry fails (so in total max. 4 requests). + +### `retryBackoffMilliseconds` + +Type `integer` + +Default: `2000` + +#### Description + +Configures the wait time in milliseconds before executing a retry. + +#### Example 1 + +```jayvee +retryBackoff: 5000 +``` + +Waits 5s (5000 ms) before executing a retry. + +### `retryBackoffStrategy` + +Type `text` + +Default: `"exponential"` + +#### Description + +Configures the wait strategy before executing a retry. Can have values "exponential" or "linear". + +#### Example 1 + +```jayvee +retryBackoffStrategy: "linear" +``` + +Waits always the same amount of time before executing a retry. + +#### Example 2 + +```jayvee +retryBackoffStrategy: "exponential" +``` + +Exponentially increases the wait time before executing a retry. + +### `followRedirects` + +Type `boolean` + +Default: `true` + +#### Description + +Indicates, whether to follow redirects on get requests. If `false`, redirects are not followed. Default `true` + +#### Example 1 + +```jayvee +url: "tinyurl.com/4ub9spwz" + followRedirects: true +``` + +Specifies the URL to fetch the data from and allows redirects. diff --git a/apps/docs/versioned_docs/version-0.1.0/user/block-types/HttpExtractor.md.license b/apps/docs/versioned_docs/version-0.1.0/user/block-types/HttpExtractor.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/block-types/HttpExtractor.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.1.0/user/block-types/PostgresLoader.md b/apps/docs/versioned_docs/version-0.1.0/user/block-types/PostgresLoader.md new file mode 100644 index 00000000..5dec79d9 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/block-types/PostgresLoader.md @@ -0,0 +1,78 @@ +--- +title: PostgresLoader +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Table` + +Output type: `None` + +## Description + +Loads a `Table` into a PostgreSQL database sink. + +## Example 1 + +```jayvee +block CarsLoader oftype PostgresLoader { + host: "localhost"; + port: 5432; + username: "postgres"; + password: "postgres"; + database: "CarsDB"; + table: "Cars"; +} +``` + +A local Postgres instance is filled with table data about cars. + +## Properties + +### `host` + +Type `text` + +#### Description + +The hostname or IP address of the Postgres database. + +### `port` + +Type `integer` + +#### Description + +The port of the Postgres database. + +### `username` + +Type `text` + +#### Description + +The username to login to the Postgres database. + +### `password` + +Type `text` + +#### Description + +The password to login to the Postgres database. + +### `database` + +Type `text` + +#### Description + +The database to use. + +### `table` + +Type `text` + +#### Description + +The name of the table to write into. diff --git a/apps/docs/versioned_docs/version-0.1.0/user/block-types/PostgresLoader.md.license b/apps/docs/versioned_docs/version-0.1.0/user/block-types/PostgresLoader.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/block-types/PostgresLoader.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.1.0/user/block-types/RowDeleter.md b/apps/docs/versioned_docs/version-0.1.0/user/block-types/RowDeleter.md new file mode 100644 index 00000000..9a2ea071 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/block-types/RowDeleter.md @@ -0,0 +1,53 @@ +--- +title: RowDeleter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Sheet` + +Output type: `Sheet` + +## Description + +Deletes one or more rows from a `Sheet`. Row IDs of subsequent rows will be shifted accordingly, so there will be no gaps. + +## Example 1 + +```jayvee +block SecondRowDeleter oftype RowDeleter { + delete: [row 2]; +} +``` + +Deletes row 2 (i.e. the second row). + +## Properties + +### `delete` + +Type `Collection<CellRange>` + +#### Description + +The rows to delete. + +#### Validation + +You need to specify at least one row. + +#### Example 1 + +```jayvee +delete: [row 2] +``` + +Delete row 2. + +#### Example 2 + +```jayvee +delete: [row 2, row 3] +``` + +Delete row 2 and row 3. diff --git a/apps/docs/versioned_docs/version-0.1.0/user/block-types/RowDeleter.md.license b/apps/docs/versioned_docs/version-0.1.0/user/block-types/RowDeleter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/block-types/RowDeleter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.1.0/user/block-types/SQLiteLoader.md b/apps/docs/versioned_docs/version-0.1.0/user/block-types/SQLiteLoader.md new file mode 100644 index 00000000..5a6d8133 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/block-types/SQLiteLoader.md @@ -0,0 +1,52 @@ +--- +title: SQLiteLoader +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Table` + +Output type: `None` + +## Description + +Loads a `Table` into a SQLite database sink. + +## Example 1 + +```jayvee +block CarsLoader oftype SQLiteLoader { + table: "cars"; + file: "./cars.db"; +} +``` + +A SQLite file `cars.db` is created in the working directory. Incoming data is written to the table `cars`. + +## Properties + +### `table` + +Type `text` + +#### Description + +The name of the table to write into. + +### `file` + +Type `text` + +#### Description + +The path to a SQLite file that will be created if it does not exist. Usual file extensions are `.sqlite` and `.db`. + +### `dropTable` + +Type `boolean` + +Default: `true` + +#### Description + +Indicates, whether to drop the table before loading data into it. If `false`, data is appended to the table instead of dropping it. diff --git a/apps/docs/versioned_docs/version-0.1.0/user/block-types/SQLiteLoader.md.license b/apps/docs/versioned_docs/version-0.1.0/user/block-types/SQLiteLoader.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/block-types/SQLiteLoader.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.1.0/user/block-types/SheetPicker.md b/apps/docs/versioned_docs/version-0.1.0/user/block-types/SheetPicker.md new file mode 100644 index 00000000..8e710e24 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/block-types/SheetPicker.md @@ -0,0 +1,33 @@ +--- +title: SheetPicker +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Workbook` + +Output type: `Sheet` + +## Description + +Selects one `Sheet` from a `Workbook` based on its `sheetName`. If no sheet matches the name, no output is created and the execution of the pipeline is aborted. + +## Example 1 + +```jayvee +block AgencySheetPicker oftype SheetPicker { + sheetName: "AgencyNames"; +} +``` + +Tries to pick the sheet `AgencyNames` from the provided `Workbook`. If `AgencyNames` exists it is passed on as `Sheet`, if it does not exist the execution of the pipeline is aborted. + +## Properties + +### `sheetName` + +Type `text` + +#### Description + +The name of the sheet to select. diff --git a/apps/docs/versioned_docs/version-0.1.0/user/block-types/SheetPicker.md.license b/apps/docs/versioned_docs/version-0.1.0/user/block-types/SheetPicker.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/block-types/SheetPicker.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.1.0/user/block-types/TableInterpreter.md b/apps/docs/versioned_docs/version-0.1.0/user/block-types/TableInterpreter.md new file mode 100644 index 00000000..455cabdf --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/block-types/TableInterpreter.md @@ -0,0 +1,89 @@ +--- +title: TableInterpreter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Sheet` + +Output type: `Table` + +## Description + +Interprets a `Sheet` as a `Table`. In case a header row is present in the sheet, its names can be matched with the provided column names. Otherwise, the provided column names are assigned in order. + +## Example 1 + +```jayvee +block CarsTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "name" oftype text, + "mpg" oftype decimal, + "cyl" oftype integer, + ]; +} +``` + +Interprets a `Sheet` about cars with a topmost header row and interprets it as a `Table` by assigning a primitive valuetype to each column. The column names are matched to the header, so the order of the type assignments does not matter. + +## Example 2 + +```jayvee +block CarsTableInterpreter oftype TableInterpreter { + header: false; + columns: [ + "name" oftype text, + "mpg" oftype decimal, + "cyl" oftype integer, + ]; +} +``` + +Interprets a `Sheet` about cars without a topmost header row and interprets it as a `Table` by sequentially assigning a name and a primitive valuetype to each column of the sheet. Note that the order of columns matters here. The first column (column `A`) will be named "name", the second column (column `B`) will be named "mpg" etc. + +## Properties + +### `header` + +Type `boolean` + +#### Description + +Whether the first row should be interpreted as header row. + +#### Example 1 + +```jayvee +header: true +``` + +The first row is interpreted as table header. The values in the header row will become the column names of the table. + +#### Example 2 + +```jayvee +header: false +``` + +The first row is NOT interpreted as table header and columns of the sheet are directly mapped to table columns. The column names are taken form the provided names in the `columns` property. + +### `columns` + +Type `Collection<ValuetypeAssignment>` + +#### Description + +Collection of valuetype assignments. Uses column names (potentially matched with the header or by sequence depending on the `header` property) to assign a primitive valuetype to each column. + +#### Validation + +Needs to be a collection of valuetype assignments. Each column needs to have a unique name. + +#### Example 1 + +```jayvee +columns: [ "name" oftype text ] +``` + +There is one column with the header "name". All values in this colum are typed as text. diff --git a/apps/docs/versioned_docs/version-0.1.0/user/block-types/TableInterpreter.md.license b/apps/docs/versioned_docs/version-0.1.0/user/block-types/TableInterpreter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/block-types/TableInterpreter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.1.0/user/block-types/TableTransformer.md b/apps/docs/versioned_docs/version-0.1.0/user/block-types/TableTransformer.md new file mode 100644 index 00000000..54d02967 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/block-types/TableTransformer.md @@ -0,0 +1,79 @@ +--- +title: TableTransformer +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Table` + +Output type: `Table` + +## Description + +Applies a transform on each value of a column. The input port type of the used transform has to match the type of the input column. + +## Example 1 + +```jayvee + +transform CelsiusToFahrenheit { + from Celsius oftype decimal; + to Fahrenheit oftype decimal; + + Fahrenheit: (Celsius * 9/5) + 32; +} + +block CelsiusToFahrenheitTransformer oftype TableTransformer { + inputColumns: ['temperature']; + outputColumn: 'temperature'; + use: CelsiusToFahrenheit; +} +``` + +Given a column "temperature" with temperature values in Celsius, it overwrites the column with computed values in Fahrenheit by using the `CelsiusToFahrenheit` transform. The transform itself is defined elsewhere in the model. + +## Example 2 + +```jayvee + +transform CelsiusToFahrenheit { + from Celsius oftype decimal; + to Fahrenheit oftype decimal; + + Fahrenheit: (Celsius * 9/5) + 32; +} + +block CelsiusToFahrenheitTransformer oftype TableTransformer { + inputColumns: ['temperatureCelsius']; + outputColumn: 'temperatureFahrenheit'; + use: CelsiusToFahrenheit; +} +``` + +Given a column "temperatureCelsius" with temperature values in Celsius, it adds a new column "temperatureFahrenheit" with computed values in Fahrenheit by using the `CelsiusToFahrenheit` transform. The transform itself is defined elsewhere in the model. + +## Properties + +### `inputColumns` + +Type `Collection<text>` + +#### Description + +The names of the input columns. The columns have to be present in the table and match with the transform's input port types. + +### `outputColumn` + +Type `text` + +#### Description + +The name of the output column. Overwrites the column if it already exists, or otherwise creates a new one. + +### `use` + +Type `Transform` + +#### Description + +Reference to the transform that is applied to the column. diff --git a/apps/docs/versioned_docs/version-0.1.0/user/block-types/TableTransformer.md.license b/apps/docs/versioned_docs/version-0.1.0/user/block-types/TableTransformer.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/block-types/TableTransformer.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.1.0/user/block-types/TextFileInterpreter.md b/apps/docs/versioned_docs/version-0.1.0/user/block-types/TextFileInterpreter.md new file mode 100644 index 00000000..0fefdcc2 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/block-types/TextFileInterpreter.md @@ -0,0 +1,35 @@ +--- +title: TextFileInterpreter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `File` + +Output type: `TextFile` + +## Description + +Interprets a `File` as a `TextFile`. + +## Properties + +### `encoding` + +Type `text` + +Default: `"utf-8"` + +#### Description + +The encoding used for decoding the file contents. + +### `lineBreak` + +Type `Regex` + +Default: `{}` + +#### Description + +The regex for identifying line breaks. diff --git a/apps/docs/versioned_docs/version-0.1.0/user/block-types/TextFileInterpreter.md.license b/apps/docs/versioned_docs/version-0.1.0/user/block-types/TextFileInterpreter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/block-types/TextFileInterpreter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.1.0/user/block-types/TextLineDeleter.md b/apps/docs/versioned_docs/version-0.1.0/user/block-types/TextLineDeleter.md new file mode 100644 index 00000000..b36a46c4 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/block-types/TextLineDeleter.md @@ -0,0 +1,23 @@ +--- +title: TextLineDeleter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `TextFile` + +Output type: `TextFile` + +## Description + +Deletes individual lines from a `TextFile`. + +## Properties + +### `lines` + +Type `Collection<integer>` + +#### Description + +The line numbers to delete. diff --git a/apps/docs/versioned_docs/version-0.1.0/user/block-types/TextLineDeleter.md.license b/apps/docs/versioned_docs/version-0.1.0/user/block-types/TextLineDeleter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/block-types/TextLineDeleter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.1.0/user/block-types/TextRangeSelector.md b/apps/docs/versioned_docs/version-0.1.0/user/block-types/TextRangeSelector.md new file mode 100644 index 00000000..ffdbebd0 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/block-types/TextRangeSelector.md @@ -0,0 +1,27 @@ +--- +title: TextRangeSelector +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `TextFile` + +Output type: `TextFile` + +## Description + +Selects a range of lines from a `TextFile`. + +## Properties + +### `lineFrom` + +Type `integer` + +Default: `1` + +### `lineTo` + +Type `integer` + +Default: `null` diff --git a/apps/docs/versioned_docs/version-0.1.0/user/block-types/TextRangeSelector.md.license b/apps/docs/versioned_docs/version-0.1.0/user/block-types/TextRangeSelector.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/block-types/TextRangeSelector.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.1.0/user/block-types/XLSXInterpreter.md b/apps/docs/versioned_docs/version-0.1.0/user/block-types/XLSXInterpreter.md new file mode 100644 index 00000000..b16befde --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/block-types/XLSXInterpreter.md @@ -0,0 +1,24 @@ +--- +title: XLSXInterpreter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `File` + +Output type: `Workbook` + +## Description + +Interprets an input file as a XLSX-file and outputs a `Workbook` containing `Sheet`s. + +## Example 1 + +```jayvee +block AgencyXLSXInterpreter oftype XLSXInterpreter { + } +``` + +Interprets an input file as a XLSX-file and outputs a `Workbook` containing `Sheet`s. + +## Properties diff --git a/apps/docs/versioned_docs/version-0.1.0/user/block-types/XLSXInterpreter.md.license b/apps/docs/versioned_docs/version-0.1.0/user/block-types/XLSXInterpreter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/block-types/XLSXInterpreter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.1.0/user/block-types/_category_.json b/apps/docs/versioned_docs/version-0.1.0/user/block-types/_category_.json new file mode 100644 index 00000000..740dd2f2 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/block-types/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Block Types", + "position": 3, + "link": { + "type": "generated-index", + "description": "These blocks are shipped with Jayvee and are available right out of the box." + } +} diff --git a/apps/docs/versioned_docs/version-0.1.0/user/block-types/_category_.json.license b/apps/docs/versioned_docs/version-0.1.0/user/block-types/_category_.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/block-types/_category_.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.1.0/user/composite-blocks.md b/apps/docs/versioned_docs/version-0.1.0/user/composite-blocks.md new file mode 100644 index 00000000..f5c9bdb5 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/composite-blocks.md @@ -0,0 +1,115 @@ +--- +sidebar_position: 4 +--- + +# Composite Blocks + +Composite blocks are a way to create new blocktypes in Jayvee by combining the functionality of existing blocks and pipes. By relying on composite blocks instead of implementing more builtin blocks in a language interpreter, Jayvee supports easy extension by users. + +Composite blocks define: +- with the `property` keyword: properties with a name and [value type](./core-concepts.md#valuetypes), optionally a default value +- with the `input` keyword: one input with a name and io type (that can be None) +- with the `output` keyword: one output with a name and io type (that can be None) +- one pipeline definition, starting from the input (using its name) and ending in the output (again using its name) +- all blocks that are used in the pipeline definition (either builtin or other composite blocks) + +## Example +As an example, the common use-case of extracting a CSV file from a webserver using HTTP. With builtin blocks, a pipeline would start with a HttpExtractor source that downloads a file from the internet and outputs a binary file. This file must be interpreted as text (using a TextFileInterpreter) and finally as Sheet (using a CSVInterpreter). + +### Implementation with builtin blocks +```mermaid +flowchart LR + A[HttpExtractor] --> B(TextFileInterpreter) + B --> C(CSVInterpreter) + C --> D(TableInterpreter) + D --> E[SQLiteSink] +``` + +A pipeline with builtin blocks is very verbose: + +```jayvee +pipeline CarsPipeline { + CarsExtractor + -> CarsTextFileInterpreter + -> CarsCSVInterpreter + -> CarsTableInterpreter + -> CarsLoader; + + block CarsExtractor oftype HttpExtractor { + url: "https://example.com/cars.csv"; + } + + block CarsTextFileInterpreter oftype TextFileInterpreter { } + + block CarsCSVInterpreter oftype CSVInterpreter { + enclosing: '"'; + } + // ... further block definitions +} +``` + +### Refactoring using composite blocks + +The common use-case of downloading a CSV file using HTTP can be refactored into a composite block. Note that we define all properties of the builtin blocks that are used as properties of the new CSVExtractor blocktype (but add fallback values). If some internal configuration is always the same, we could also not expose it as a property of the new blocktype. + +```jayvee +// Define a new blocktype named CSVExtractor outside of the pipeline +composite blocktype CSVExtractor { + // Properties of the CSVExtractor, some with default values + property url oftype text; + property delimiter oftype text: ','; + property enclosing oftype text: ''; + property enclosingEscape oftype text: ''; + + // Input and outputs + input inputName oftype None; + output outputName oftype Sheet; + + // Pipeline definition from input, over blocks defined later, to output + inputName + ->FileExtractor + ->FileTextInterpreter + ->FileCSVInterpreter + ->outputName; + + // Block definitions using values from properties by name + block FileExtractor oftype HttpExtractor { url: url; } + block FileTextInterpreter oftype TextFileInterpreter {} + + block FileCSVInterpreter oftype CSVInterpreter { + delimiter: delimiter; + enclosing: enclosing; + enclosingEscape: enclosingEscape; + } +} +``` + +With the new CSVExtractor composite blocktype, the pipeline now looks like this. + +```mermaid +flowchart LR + CSVExtractor --> D(TableInterpreter) + D --> E[SQLiteSink] + + subgraph CSVExtractor + A[HttpExtractor] --> B(TextFileInterpreter) + B --> C(CSVInterpreter) +end +``` + +If the CSVExtractor is available in the scope of the `CarsPipeline` from before (e.g., by defining it above the pipeline), it can then be used to shorten the actual pipeline code. + +```jayvee +pipeline CarsPipeline { + // HttpExtractor, TextFileInterpreter and CSVInterpreter have been replaced by CSVExtractor + CSVExtractor + -> CarsTableInterpreter + -> CarsLoader; + + block CarsExtractor oftype CSVExtractor { + url: "https://example.com/cars.csv"; + } + + // ... further block definitions +} +``` \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.1.0/user/composite-blocks.md.license b/apps/docs/versioned_docs/version-0.1.0/user/composite-blocks.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/composite-blocks.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.1.0/user/constraint-types/AllowlistConstraint.md b/apps/docs/versioned_docs/version-0.1.0/user/constraint-types/AllowlistConstraint.md new file mode 100644 index 00000000..76dd9df1 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/constraint-types/AllowlistConstraint.md @@ -0,0 +1,27 @@ +--- +title: AllowlistConstraint +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Compatible ValueType: text + +## Description + +Limits the values to a defined a set of allowed values. Only values in the list are valid. + +## Example 1 + +```jayvee +constraint TimeUnitString oftype AllowlistConstraint { + allowlist: ["ms", "s", "min", "h", "d", "m", "y"]; +} +``` + +Only allows the common abbreviations for millisecond, second, minute, etc.. + +## Properties + +### `allowlist` + +Type `Collection<text>` diff --git a/apps/docs/versioned_docs/version-0.1.0/user/constraint-types/AllowlistConstraint.md.license b/apps/docs/versioned_docs/version-0.1.0/user/constraint-types/AllowlistConstraint.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/constraint-types/AllowlistConstraint.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.1.0/user/constraint-types/DenylistConstraint.md b/apps/docs/versioned_docs/version-0.1.0/user/constraint-types/DenylistConstraint.md new file mode 100644 index 00000000..078f57f2 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/constraint-types/DenylistConstraint.md @@ -0,0 +1,27 @@ +--- +title: DenylistConstraint +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Compatible ValueType: text + +## Description + +Defines a set of forbidden values. All values in the list are considered invalid. + +## Example 1 + +```jayvee +constraint NoPrimaryColors oftype DenylistConstraint { + denylist: ["red", "blue", "yellow"]; +} +``` + +Denies all primary colors. + +## Properties + +### `denylist` + +Type `Collection<text>` diff --git a/apps/docs/versioned_docs/version-0.1.0/user/constraint-types/DenylistConstraint.md.license b/apps/docs/versioned_docs/version-0.1.0/user/constraint-types/DenylistConstraint.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/constraint-types/DenylistConstraint.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.1.0/user/constraint-types/LengthConstraint.md b/apps/docs/versioned_docs/version-0.1.0/user/constraint-types/LengthConstraint.md new file mode 100644 index 00000000..13234897 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/constraint-types/LengthConstraint.md @@ -0,0 +1,36 @@ +--- +title: LengthConstraint +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Compatible ValueType: text + +## Description + +Limits the length of a string with an upper and/or lower boundary. Only values with a length within the given range are valid. + +## Example 1 + +```jayvee +constraint JavaStringLength oftype LengthConstraint { + minLength: 0; + maxLength: 2147483647; +} +``` + +A string with 0 to 2147483647 characters. + +## Properties + +### `minLength` + +Type `integer` + +Default: `0` + +### `maxLength` + +Type `integer` + +Default: `null` diff --git a/apps/docs/versioned_docs/version-0.1.0/user/constraint-types/LengthConstraint.md.license b/apps/docs/versioned_docs/version-0.1.0/user/constraint-types/LengthConstraint.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/constraint-types/LengthConstraint.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.1.0/user/constraint-types/RangeConstraint.md b/apps/docs/versioned_docs/version-0.1.0/user/constraint-types/RangeConstraint.md new file mode 100644 index 00000000..1539f2e7 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/constraint-types/RangeConstraint.md @@ -0,0 +1,60 @@ +--- +title: RangeConstraint +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Compatible ValueType: decimal + +## Description + +Limits the range of a number value with an upper and/or lower boundary which can be inclusive or exclusive. Only values within the given range are considered valid. + +## Example 1 + +```jayvee +constraint HundredScale oftype RangeConstraint { + lowerBound: 1; + upperBound: 100; +} +``` + +A scale between (and including) 1 and 100. + +## Example 2 + +```jayvee +constraint HundredScale oftype RangeConstraint { + lowerBound: 1; + lowerBoundInclusive: false; + upperBound: 100; +} +``` + +A scale for numbers strictly larger than 1 and less or equal to 100. + +## Properties + +### `lowerBound` + +Type `decimal` + +Default: `null` + +### `lowerBoundInclusive` + +Type `boolean` + +Default: `true` + +### `upperBound` + +Type `decimal` + +Default: `null` + +### `upperBoundInclusive` + +Type `boolean` + +Default: `true` diff --git a/apps/docs/versioned_docs/version-0.1.0/user/constraint-types/RangeConstraint.md.license b/apps/docs/versioned_docs/version-0.1.0/user/constraint-types/RangeConstraint.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/constraint-types/RangeConstraint.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.1.0/user/constraint-types/RegexConstraint.md b/apps/docs/versioned_docs/version-0.1.0/user/constraint-types/RegexConstraint.md new file mode 100644 index 00000000..deb94a4b --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/constraint-types/RegexConstraint.md @@ -0,0 +1,27 @@ +--- +title: RegexConstraint +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Compatible ValueType: text + +## Description + +Limits the values complying with a regex. Only values that comply with the regex are considered valid. + +## Example 1 + +```jayvee +constraint IFOPT_Format oftype RegexConstraint { + regex: /[a-z]{2}:\d+:\d+(:\d+)?(:\d+)?/; +} +``` + +Text that complies with the IFOPT (Identification of Fixed Objects in Public Transport) DIN EN 28701:2012 format. + +## Properties + +### `regex` + +Type `Regex` diff --git a/apps/docs/versioned_docs/version-0.1.0/user/constraint-types/RegexConstraint.md.license b/apps/docs/versioned_docs/version-0.1.0/user/constraint-types/RegexConstraint.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/constraint-types/RegexConstraint.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.1.0/user/constraint-types/_category_.json b/apps/docs/versioned_docs/version-0.1.0/user/constraint-types/_category_.json new file mode 100644 index 00000000..cd8b1300 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/constraint-types/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Constraint Types", + "position": 6, + "link": { + "type": "generated-index", + "description": "These constraints are shipped with Jayvee and are available right out of the box." + } +} diff --git a/apps/docs/versioned_docs/version-0.1.0/user/constraint-types/_category_.json.license b/apps/docs/versioned_docs/version-0.1.0/user/constraint-types/_category_.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/constraint-types/_category_.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.1.0/user/core-concepts.md b/apps/docs/versioned_docs/version-0.1.0/user/core-concepts.md new file mode 100644 index 00000000..eba14831 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/core-concepts.md @@ -0,0 +1,111 @@ +--- +sidebar_position: 2 +--- + +# Core Concepts + +The core concepts of Jayvee are `Pipelines`, `Blocks`, and `ValueTypes`. + +## Pipelines + +A `Pipeline` is a sequence of different computing steps, the `Blocks`. +The default output of a block becomes the default input of the next block, building a chain of computing steps. +In the scope of a `Pipeline`, you can connect these blocks via the `pipe` syntax: + +```jayvee +pipeline CarsPipeline { + // Assumption: blocks "GasReserveHttpExtractor", "GasReserveCSVInterpreter", "GasReserveTableInterpreter", and "GasReserveLoader" are defined + + GasReserveHttpExtractor + -> GasReserveTextFileInterpreter + -> GasReserveCSVInterpreter + -> GasReserveTableInterpreter + -> GasReserveLoader; +} +``` + +Alternatively, you can use a slightly longer syntax for pipes: + +```jayvee +pipeline CarsPipeline { + // Assumption: blocks "GasReserveHttpExtractor", "GasReserveCSVInterpreter", "GasReserveTableInterpreter", and "GasReserveLoader" are defined + + pipe { + from: GasReserveHttpExtractor; + to: GasReserveTextFileInterpreter; + + } + + pipe { + from: GasReserveTextFileInterpreter; + to: GasReserveCSVInterpreter; + + } + + // etc. +} +``` + +## Blocks + +A `Block` is a processing step within a `Pipeline`. +It can have a default input and a default output. +We differentiate the following types of `Blocks`: +- `ExtractorBlocks` do not have a default input but only a default output. They model a **data source**. +- `TransformatorBlocks` have a default input and a default output. They model a **transformation**. +- `LoaderBlocks` do have a default input but nor a default output. They model a **data sink**. + +The general structure of a `Pipeline` consisting of different blocks is the following: + +```mermaid +flowchart LR + A[ExtractorBlock] --> B(TransformatorBlock) + B --> C(TransformatorBlock) + C --> D(LoaderBlock) +``` + +The common syntax of blocks is at its core a key-value map to provide configuration to the block. +The availability of property keys and their respective `ValueTypes` is determined by the type of the `Block` - indicated by the identifier after the keyword `oftype`: + +```jayvee +block GasReserveHttpExtractor oftype HttpExtractor { + // key: value + url: "https://www.bundesnetzagentur.de/_tools/SVG/js2/_functions/csv_export.html?view=renderCSV&id=1089590"; +} +``` + +In the example above, the `url` property of type `text` is defined by the corresponding `HttpExtractor` block type. + +Blocks can be either defined as part of the language, called `builtin` or defined as composition of existing blocks by users in Jayvee, called `composite`. See the documentation for [Composite Blocks](./composite-blocks.md). + +## ValueTypes + +A `ValueType` is the definition of a data type of the processed data. +Some `Blocks` use `ValueTypes` to define logic (like filtering or assessing the data type in a data sink). +We differentiate the following types of `ValueTypes`: +- `Built-in ValueTypes` come with the basic version of Jayvee. See [Built-in Valuetypes](./valuetypes/builtin-valuetypes). +- `Primitive ValueTypes` can be defined by the user to model domain-specific data types and represent a single value. + `Constraints` can be added to a `Primitive ValueType`. +See [Primitive Valuetypes](./valuetypes/primitive-valuetypes). +- `Compound ValueTypes`: UPCOMING. + +```jayvee +valuetype GasFillLevel oftype integer { + constraints: [ GasFillLevelRange ]; +} + +constraint GasFillLevelRange on decimal: + value >= 0 and value <= 100; +``` + +## Transforms +`Transforms` are used to transform data from one `ValueType` to a different one. For more details, see [Transforms](./transforms.md). + +```jayvee +transform CelsiusToKelvin { + from tempCelsius oftype decimal; + to tempKelvin oftype decimal; + + tempKelvin: tempCelsius + 273.15; +} +``` \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.1.0/user/core-concepts.md.license b/apps/docs/versioned_docs/version-0.1.0/user/core-concepts.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/core-concepts.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.1.0/user/examples/_category_.json b/apps/docs/versioned_docs/version-0.1.0/user/examples/_category_.json new file mode 100644 index 00000000..65abd42e --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/examples/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Jayvee Examples", + "position": 10, + "link": { + "type": "generated-index", + "description": "Examples of Jayvee models" + } +} diff --git a/apps/docs/versioned_docs/version-0.1.0/user/examples/_category_.json.license b/apps/docs/versioned_docs/version-0.1.0/user/examples/_category_.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/examples/_category_.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.1.0/user/examples/cars.md b/apps/docs/versioned_docs/version-0.1.0/user/examples/cars.md new file mode 100644 index 00000000..e53ede09 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/examples/cars.md @@ -0,0 +1,115 @@ +--- +title: cars +--- + +```jayvee +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// Example 1: Cars +// Learning goals: +// - Understand the core concepts pipeline, block, and pipe +// - Understand the general structure of a pipeline + +// 1. This Jayvee model describes a pipeline +// from a CSV file in the web +// to a SQLite file sink. +pipeline CarsPipeline { + + // 2. We describe the structure of the pipeline, + // usually at the top of the pipeline. + // by connecting blocks via pipes. + + // 3. Verbose syntax of a pipe + // connecting the block CarsExtractor + // with the block CarsTextFileInterpreter. + pipe { + from: CarsExtractor; + to: CarsTextFileInterpreter; + } + + // 4. The output of the "from" block is hereby used + // as input for the "to" block. + + // 5. More convenient syntax of a pipe + CarsTextFileInterpreter -> CarsCSVInterpreter; + + // 6. Pipes can be further chained, + // leading to an overview of the pipeline. + CarsCSVInterpreter + -> NameHeaderWriter + -> CarsTableInterpreter + -> CarsLoader; + + + // 7. Below the pipes, we usually define the blocks + // that are connected by the pipes. + + // 8. Blocks instantiate a blocktype by using the oftype keyword. + // The blocktype defines the available properties that the block + // can use to specify the intended behavior of the block + block CarsExtractor oftype HttpExtractor { + + // 9. Properties are assigned to concrete values. + // Here, we specify the URL where the file shall be downloaded from. + url: "https://gist.githubusercontent.com/noamross/e5d3e859aa0c794be10b/raw/b999fb4425b54c63cab088c0ce2c0d6ce961a563/cars.csv"; + } + + // 10. The HttpExtractor requires no input and produces a binary file as output. + // This file has to be interpreted, e.g., as text file. + block CarsTextFileInterpreter oftype TextFileInterpreter { } + + // 11. Next, we interpret the text file as sheet. + // A sheet only contains text cells and is useful for manipulating the shape of data before assigning more strict value types to cells. + block CarsCSVInterpreter oftype CSVInterpreter { + enclosing: '"'; + } + + // 12. We can write into cells of a sheet using the CellWriter blocktype. + block NameHeaderWriter oftype CellWriter { + // 13. We utilize a syntax similar to spreadsheet programs. + // Cell ranges can be described using the keywords "cell", "row", "column", or "range" that indicate which + // cells are selected for the write action. + at: cell A1; + + // 14. For each cell we selected with the "at" property above, + // we can specify what value shall be written into the cell. + write: ["name"]; + } + + // 15. As a next step, we interpret the sheet as a table by adding structure. + // We define a valuetype per column that specifies the data type of the column. + // Rows that include values that are not valid according to the their valuetypes are dropped automatically. + block CarsTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "name" oftype text, + "mpg" oftype decimal, + "cyl" oftype integer, + "disp" oftype decimal, + "hp" oftype integer, + "drat" oftype decimal, + "wt" oftype decimal, + "qsec" oftype decimal, + "vs" oftype integer, + "am" oftype integer, + "gear" oftype integer, + "carb" oftype integer + ]; + } + + // 16. As a last step, we load the table into a sink, + // here into a sqlite file. + // The structural information of the table is used + // to generate the correct table. + block CarsLoader oftype SQLiteLoader { + table: "Cars"; + file: "./cars.sqlite"; + } + + // 17. Congratulations! + // You can now use the sink for your data analysis, app, + // or whatever you want to do with the cleaned data. +} +``` \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.1.0/user/examples/cars.md.license b/apps/docs/versioned_docs/version-0.1.0/user/examples/cars.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/examples/cars.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.1.0/user/examples/composite-block.md b/apps/docs/versioned_docs/version-0.1.0/user/examples/composite-block.md new file mode 100644 index 00000000..8151c7c5 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/examples/composite-block.md @@ -0,0 +1,98 @@ +--- +title: composite-block +--- + +```jayvee +composite blocktype TextFileExtractor { + property url oftype text; + + input inputName oftype None; + output outputName oftype File; + + block FileExtractor oftype HttpExtractor { url: url; } + + block FileTextInterpreter oftype TextFileInterpreter {} + + inputName + ->FileExtractor + ->FileTextInterpreter + ->outputName; +} + +composite blocktype CSVExtractor { + property url oftype text; + property delimiter oftype text: ','; + property enclosing oftype text: ''; + property enclosingEscape oftype text: ''; + + input inputName oftype None; + output outputName oftype Sheet; + + block TextFileExtractor oftype TextFileExtractor { url: url; } + + block FileCSVInterpreter oftype CSVInterpreter { + delimiter: delimiter; + enclosing: enclosing; + enclosingEscape: enclosingEscape; + } + + inputName + ->TextFileExtractor + ->FileCSVInterpreter + ->outputName; +} + +composite blocktype CarsTableInterpreterForTesting { + input inputName oftype Sheet; + output outputName oftype Table; + + block CarsTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "name" oftype text, + "mpg" oftype decimal, + "cyl" oftype integer, + "disp" oftype decimal, + "hp" oftype integer, + "drat" oftype decimal, + "wt" oftype decimal, + "qsec" oftype decimal, + "vs" oftype integer, + "am" oftype integer, + "gear" oftype integer, + "carb" oftype integer + ]; + } + + inputName->CarsTableInterpreter->outputName; +} + +composite blocktype SinkTester { + input inputName oftype Table; + output outputName oftype None; + + block CarsLoader oftype SQLiteLoader { + table: "Cars"; + file: "./compositeblockstest.sqlite"; + } + + inputName->CarsLoader->outputName; +} + +pipeline CarsPipeline { + CarsCSVExtractor + -> TestCompositeWithInput + -> CarsLoader; + + block CarsCSVExtractor oftype CSVExtractor { + url: "https://gist.githubusercontent.com/noamross/e5d3e859aa0c794be10b/raw/b999fb4425b54c63cab088c0ce2c0d6ce961a563/cars.csv"; + enclosing: '"'; + } + + block TestCompositeWithInput oftype CarsTableInterpreterForTesting { + } + + block CarsLoader oftype SinkTester { + } +} +``` \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.1.0/user/examples/composite-block.md.license b/apps/docs/versioned_docs/version-0.1.0/user/examples/composite-block.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/examples/composite-block.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.1.0/user/examples/electric-vehicles.md b/apps/docs/versioned_docs/version-0.1.0/user/examples/electric-vehicles.md new file mode 100644 index 00000000..99398633 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/examples/electric-vehicles.md @@ -0,0 +1,150 @@ +--- +title: electric-vehicles +--- + +```jayvee +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// Example 2: Electric Vehicles +// Learning goals: +// - Understand further core concepts transforms and valuetypes +// - Understand how to construct a pipeline with multiple sinks +// - Understand the use of runtime parameters + +// 1. This Jayvee model describes a pipeline +// from a CSV file in the web +// to a SQLite file and a PostgreSQL db sink. +pipeline ElectricVehiclesPipeline { + // See here for meta-data of the data source + // https://catalog.data.gov/dataset/electric-vehicle-population-data/resource/fa51be35-691f-45d2-9f3e-535877965e69 + + // 2. At the top of a pipeline, we describe the + // structure of the pipeline. The first part until + // the ElectricRangeTransformer is a linear sequence + // of blocks. From there we can see a split into two + // parallel sequences that load the data in to two + // different sinks. + ElectricVehiclesHttpExtractor + -> ElectricVehiclesTextFileInterpreter + -> ElectricVehiclesCSVInterpreter + -> ElectricVehiclesTableInterpreter + -> ElectricRangeTransformer; + + ElectricRangeTransformer + -> ElectricVehiclesSQLiteLoader; + + ElectricRangeTransformer + -> ElectricVehiclesPostgresLoader; + + // 3. After the pipeline structure, we define the blocks used. + block ElectricVehiclesHttpExtractor oftype HttpExtractor { + url: "https://data.wa.gov/api/views/f6w7-q2d2/rows.csv?accessType=DOWNLOAD"; + } + + block ElectricVehiclesTextFileInterpreter oftype TextFileInterpreter { } + + block ElectricVehiclesCSVInterpreter oftype CSVInterpreter { } + + block ElectricVehiclesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + // 4. Here, a user-deifned valuetype is used to describe this column. + // The capital letter indicates that the valuetype is not builtin + // by convention. The valuetype itself is defined further below. + "VIN (1-10)" oftype VehicleIdentificationNumber10, + "County" oftype text, + "City" oftype text, + "State" oftype UsStateCode, + "Postal Code" oftype text, + "Model Year" oftype integer, + "Make" oftype text, + "Model" oftype text, + "Electric Vehicle Type" oftype text, + "Clean Alternative Fuel Vehicle (CAFV) Eligibility" oftype text, + "Electric Range" oftype integer, + "Base MSRP" oftype integer, + "Legislative District" oftype text, + "DOL Vehicle ID" oftype integer, + "Vehicle Location" oftype text, + "Electric Utility" oftype text, + "2020 Census Tract" oftype text, + ]; + } + + // 5. This block describes the application of a transform function + // taking a column as input and adding another computed column. + // The applied transform function is defined below and referenced + // by the "use" property. + block ElectricRangeTransformer oftype TableTransformer { + inputColumns: ["Electric Range"]; + outputColumn: "Electric Range (km)"; + use: MilesToKilometers; + } + + // 6. Here, we define a transform function, taking parameters + // as input ("from" keyword), and producing an output ("to" keyword). + // Inputs and outputs have to be further described by a valuetype. + transform MilesToKilometers { + from miles oftype decimal; + to kilometers oftype integer; + + // 7. In order to express what the transform function does, + // we assign an expression to the output. Values from the input and output of the transform can be referred to by name. + kilometers: round (miles * 1.609344); + } + + block ElectricVehiclesSQLiteLoader oftype SQLiteLoader { + table: "ElectricVehiclePopulationData"; + file: "./electric-vehicles.sqlite"; + } + + block ElectricVehiclesPostgresLoader oftype PostgresLoader { + // 8. The requires keyword allows us to define runtime parameters. + // These values have to be provided as environment variables when interpreting the Jayvee model. + host: requires DB_HOST; + port: requires DB_PORT; + username: requires DB_USERNAME; + password: requires DB_PASSWORD; + database: requires DB_DATABASE; + table: "ElectricVehiclePopulationData"; + } +} + +// 9. Below the pipeline, we model user-define valuetypes. +// We give them a speaking name and provide a base valuetype +// that this valuetype builts on. User-defined valuetypes always place additional constraints on existing valuetypes. +valuetype VehicleIdentificationNumber10 oftype text { + // 10. Valuetypes can be further refined by providing constraints. + constraints: [ + OnlyCapitalLettersAndDigits, + ExactlyTenCharacters, + ]; +} + +// 11. This constraint works on text valuetypes and requires values +// to match a given regular expression in order to be valid. +constraint OnlyCapitalLettersAndDigits on text: + value matches /^[A-Z0-9]*$/; + +constraint ExactlyTenCharacters on text: + value.length == 10; + +valuetype UsStateCode oftype text { + constraints: [ + UsStateCodeAllowlist, + ]; +} + +constraint UsStateCodeAllowlist on text: + value in [ + "AL", "AK", "AZ", "AR", "AS", "CA", "CO", "CT", "DE", "DC", + "FL", "GA", "GU", "HI", "ID", "IL", "IN", "IA", "KS", "KY", + "LA", "ME", "MD", "MA", "MI", "MN", "MS", "MO", "MT", "NE", + "NV", "NH", "NJ", "NM", "NY", "NC", "ND", "MP", "OH", "OK", + "OR", "PA", "PR", "RI", "SC", "SD", "TN", "TX", "TT", "UT", + "VT", "VA", "VI", "WA", "WV", "WI", "WY", + ]; + +``` \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.1.0/user/examples/electric-vehicles.md.license b/apps/docs/versioned_docs/version-0.1.0/user/examples/electric-vehicles.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/examples/electric-vehicles.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.1.0/user/examples/gtfs-rt.md b/apps/docs/versioned_docs/version-0.1.0/user/examples/gtfs-rt.md new file mode 100644 index 00000000..7610d8c1 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/examples/gtfs-rt.md @@ -0,0 +1,133 @@ +--- +title: gtfs-rt +--- + +```jayvee +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// Example 3: GTFS Realtime Data +// Learning goals: +// - Understand the construction of a csv file with multiple tables +// - Understand how to work with live data + +// 1. This Jayvee model describes a pipeline +// from a GTFS RT data source in the web +// to a SQLite file with multiple tables. +pipeline GtfsRTSimplePipeline { + + // 2. As you can see here, we have three independent + // sequences of pipes in this pipeline. + GTFSRTTripUpdateFeedExtractor + ->GtfsRTTripUpdateInterpreter + ->TripUpdateTableInterpreter + ->TripUpdateLoader; + + GTFSRTVehiclePositionFeedExtractor + ->GtfsRTVehiclePositionInterpreter + ->VehiclePositionTableInterpreter + ->VehicleLoader; + + GTFSRTAlertFeedExtractor + ->GtfsRTAlertInterpreter + ->AlertTableInterpreter + ->AlertLoader; + + // 3. We define a series of HttpExtractors that each pull data + // from an HTTP endpoint + block GTFSRTTripUpdateFeedExtractor oftype HttpExtractor { + url: "https://proxy.transport.data.gouv.fr/resource/bibus-brest-gtfs-rt-trip-update"; + } + + block GTFSRTVehiclePositionFeedExtractor oftype HttpExtractor { + url: "https://proxy.transport.data.gouv.fr/resource/bibus-brest-gtfs-rt-vehicle-position"; + } + + block GTFSRTAlertFeedExtractor oftype HttpExtractor { + url: "https://proxy.transport.data.gouv.fr/resource/bibus-brest-gtfs-rt-alerts"; + } + + // 4. In the next step, we use the domain-specific GtfsRTInterpreter + // to interpret the fetched files as sheets + block GtfsRTTripUpdateInterpreter oftype GtfsRTInterpreter { + entity: "trip_update"; + } + + block GtfsRTAlertInterpreter oftype GtfsRTInterpreter { + entity: "alert"; + } + + block GtfsRTVehiclePositionInterpreter oftype GtfsRTInterpreter { + entity: "vehicle"; + } + + // 5. Next, we interpret the sheets as tables + block TripUpdateTableInterpreter oftype TableInterpreter { + header: true; + columns:[ + "header.gtfs_realtime_version" oftype text, + "header.timestamp" oftype text, + "header.incrementality" oftype text, + "entity.id" oftype text, + "entity.trip_update.trip.trip_id" oftype text, + "entity.trip_update.trip.route_id" oftype text, + "entity.trip_update.stop_time_update.stop_sequence" oftype text, + "entity.trip_update.stop_time_update.stop_id" oftype text, + "entity.trip_update.stop_time_update.arrival.time" oftype text, + "entity.trip_update.stop_time_update.departure.time" oftype text, + ]; + } + + block VehiclePositionTableInterpreter oftype TableInterpreter { + header: true; + columns:[ + "header.gtfs_realtime_version" oftype text, + "header.timestamp" oftype text, + "header.incrementality" oftype text, + "entity.id" oftype text, + "entity.vehicle_position.vehicle_descriptor.id" oftype text, + "entity.vehicle_position.trip.trip_id" oftype text, + "entity.vehicle_position.trip.route_id" oftype text, + "entity.vehicle_position.position.latitude" oftype text, + "entity.vehicle_position.position.longitude" oftype text, + "entity.vehicle_position.timestamp" oftype text + ]; + } + + block AlertTableInterpreter oftype TableInterpreter { + header: true; + columns:[ + 'header.gtfs_realtime_version' oftype text, + 'header.timestamp' oftype text, + 'header.incrementality' oftype text, + 'entity.id' oftype text, + 'entity.alert.informed_entity.route_id' oftype text, + 'entity.alert.header_text' oftype text, + 'entity.alert.description_text' oftype text, + ]; + } + + // 6. Last, we load the tables into the same SQLite file. + // Each loader has to define a different table name. + // For working with live data, we use the property "dropTable: false" + // to append data instead of deleting the previous data. + block TripUpdateLoader oftype SQLiteLoader { + table: "gtfs-rt-trip_update"; + file: "./gtfs.sqlite"; + dropTable: false; + } + + block VehicleLoader oftype SQLiteLoader { + table: "gtfs-rt-vehicle_position"; + file: "./gtfs.sqlite"; + dropTable: false; + } + + block AlertLoader oftype SQLiteLoader { + table: "gtfs-rt-alert"; + file: "./gtfs.sqlite"; + dropTable: false; + } +} +``` \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.1.0/user/examples/gtfs-rt.md.license b/apps/docs/versioned_docs/version-0.1.0/user/examples/gtfs-rt.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/examples/gtfs-rt.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.1.0/user/examples/gtfs-static.md b/apps/docs/versioned_docs/version-0.1.0/user/examples/gtfs-static.md new file mode 100644 index 00000000..ca6434df --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/examples/gtfs-static.md @@ -0,0 +1,370 @@ +--- +title: gtfs-static +--- + +```jayvee +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// Example 4: GTFS Static Data +// Learning goals: +// - Understand how to work with file systems + +// 1. This Jayvee model describes a pipeline +// from a zip file in the GTFS format in the web +// to a joint SQLite file with multiple tables. +pipeline GtfsPipeline { + + // 2. The origin for multiple pipe sequences is a zip + // file. Each csv file in this zip is further processed + // by its own sequence of blocks and pipes. + GTFSSampleFeedExtractor -> ZipArchiveInterpreter; + + ZipArchiveInterpreter + -> AgencyFilePicker + -> AgencyTextFileInterpreter + -> AgencyCSVInterpreter + -> AgencyTableInterpreter + -> AgencyLoader; + + ZipArchiveInterpreter + -> CalendarDatesFilePicker + -> CalendarDatesTextFileInterpreter + -> CalendarDatesCSVInterpreter + -> CalendarDatesTableInterpreter + -> CalendarDatesLoader; + + ZipArchiveInterpreter + -> CalendarFilePicker + -> CalendarTextFileInterpreter + -> CalendarCSVInterpreter + -> CalendarTableInterpreter + -> CalendarLoader; + + ZipArchiveInterpreter + -> FareAttributesFilePicker + -> FareAttributesTextFileInterpreter + -> FareAttributesCSVInterpreter + -> FareAttributesTableInterpreter + -> FareAttributesLoader; + + ZipArchiveInterpreter + -> FareRulesFilePicker + -> FareRulesTextFileInterpreter + -> FareRulesCSVInterpreter + -> FareRulesTableInterpreter + -> FareRulesLoader; + + ZipArchiveInterpreter + -> FrequenciesFilePicker + -> FrequenciesTextFileInterpreter + -> FrequenciesCSVInterpreter + -> FrequenciesTableInterpreter + -> FrequenciesLoader; + + ZipArchiveInterpreter + -> RoutesFilePicker + -> RoutesTextFileInterpreter + -> RoutesCSVInterpreter + -> RoutesTableInterpreter + -> RoutesLoader; + + ZipArchiveInterpreter + -> ShapesFilePicker + -> ShapesTextFileInterpreter + -> ShapesCSVInterpreter + -> ShapesTableInterpreter + -> ShapesLoader; + + ZipArchiveInterpreter + -> StopTimesFilePicker + -> StopTimesTextFileInterpreter + -> StopTimesCSVInterpreter + -> StopTimesTableInterpreter + -> StopTimesLoader; + + ZipArchiveInterpreter + -> StopsFilePicker + -> StopsTextFileInterpreter + -> StopsCSVInterpreter + -> StopsTableInterpreter + -> StopsLoader; + + ZipArchiveInterpreter + -> TripsFilePicker + -> TripsTextFileInterpreter + -> TripsCSVInterpreter + -> TripsTableInterpreter + -> TripsLoader; + + // 3. As a first step, we download the zip file and interpret it. + block GTFSSampleFeedExtractor oftype HttpExtractor { + url: "https://developers.google.com/static/transit/gtfs/examples/sample-feed.zip"; + } + + block ZipArchiveInterpreter oftype ArchiveInterpreter { + archiveType: "zip"; + } + + // 4. Next, we pick several csv files (with the file extension ".txt") + // for further processing . + block AgencyFilePicker oftype FilePicker { + path: "/agency.txt"; + } + + block CalendarDatesFilePicker oftype FilePicker { + path: "/calendar_dates.txt"; + } + + block CalendarFilePicker oftype FilePicker { + path: "/calendar.txt"; + } + + block FareAttributesFilePicker oftype FilePicker { + path: "/fare_attributes.txt"; + } + + block FareRulesFilePicker oftype FilePicker { + path: "/fare_rules.txt"; + } + + block FrequenciesFilePicker oftype FilePicker { + path: "/frequencies.txt"; + } + + block RoutesFilePicker oftype FilePicker { + path: "/routes.txt"; + } + + block ShapesFilePicker oftype FilePicker { + path: "/shapes.txt"; + } + + block StopTimesFilePicker oftype FilePicker { + path: "/stop_times.txt"; + } + + block StopsFilePicker oftype FilePicker { + path: "/stops.txt"; + } + + block TripsFilePicker oftype FilePicker { + path: "/trips.txt"; + } + + // 5. The rest of the pipeline follows the usual pattern. + block AgencyTextFileInterpreter oftype TextFileInterpreter { } + block CalendarDatesTextFileInterpreter oftype TextFileInterpreter { } + block CalendarTextFileInterpreter oftype TextFileInterpreter { } + block FareAttributesTextFileInterpreter oftype TextFileInterpreter { } + block FareRulesTextFileInterpreter oftype TextFileInterpreter { } + block FrequenciesTextFileInterpreter oftype TextFileInterpreter { } + block RoutesTextFileInterpreter oftype TextFileInterpreter { } + block ShapesTextFileInterpreter oftype TextFileInterpreter { } + block StopTimesTextFileInterpreter oftype TextFileInterpreter { } + block StopsTextFileInterpreter oftype TextFileInterpreter { } + block TripsTextFileInterpreter oftype TextFileInterpreter { } + block AgencyCSVInterpreter oftype CSVInterpreter { } + block CalendarDatesCSVInterpreter oftype CSVInterpreter { } + block CalendarCSVInterpreter oftype CSVInterpreter { } + block FareAttributesCSVInterpreter oftype CSVInterpreter { } + block FareRulesCSVInterpreter oftype CSVInterpreter { } + block FrequenciesCSVInterpreter oftype CSVInterpreter { } + block RoutesCSVInterpreter oftype CSVInterpreter { } + block ShapesCSVInterpreter oftype CSVInterpreter { } + block StopTimesCSVInterpreter oftype CSVInterpreter { } + block StopsCSVInterpreter oftype CSVInterpreter { } + block TripsCSVInterpreter oftype CSVInterpreter { } + + block AgencyTableInterpreter oftype TableInterpreter { + header: true; + columns:[ + "agency_id" oftype text, //Conditional columns are considered as required + "agency_name" oftype text, + "agency_url" oftype text, + "agency_timezone" oftype text + ]; + } + + block CalendarDatesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "service_id" oftype text, + "date" oftype text, + "exception_type" oftype text + ]; + } + + block CalendarTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "service_id" oftype text, + "monday" oftype text, + "tuesday" oftype text, + "wednesday" oftype text, + "thursday" oftype text, + "friday" oftype text, + "saturday" oftype text, + "sunday" oftype text, + "start_date" oftype text, + "end_date" oftype text + ]; + } + + block FareAttributesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "fare_id" oftype text, + "price" oftype text, + "currency_type" oftype text, + "payment_method" oftype text, + "transfers" oftype text, + "transfer_duration" oftype text + ]; + } + + block FareRulesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "fare_id" oftype text, + "route_id" oftype text, + "origin_id" oftype text, + "destination_id" oftype text, + "contains_id" oftype text + ]; + } + + block FrequenciesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "trip_id" oftype text, + "start_time" oftype text, + "end_time" oftype text, + "headway_secs" oftype text + ]; + } + + block RoutesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "route_id" oftype text, + "agency_id" oftype text, + "route_short_name" oftype text, + "route_long_name" oftype text, + "route_desc" oftype text, + "route_type" oftype text, + "route_url" oftype text, + "route_color" oftype text, + "route_text_color" oftype text + ]; + } + + block ShapesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "shape_id" oftype text, + "shape_pt_lat" oftype text, + "shape_pt_lon" oftype text, + "shape_pt_sequence" oftype text, + "shape_dist_traveled" oftype text + ]; + } + + block StopTimesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "trip_id" oftype text, + "arrival_time" oftype text, + "departure_time" oftype text, + "stop_id" oftype text, + "stop_sequence" oftype text, + "stop_headsign" oftype text, + "pickup_type" oftype text, + "drop_off_time" oftype text, + "shape_dist_traveled" oftype text + ]; + } + + block StopsTableInterpreter oftype TableInterpreter { + header: true; + columns:[ + "stop_id" oftype text, + "stop_name" oftype text, + "stop_desc" oftype text, + "stop_lat" oftype text, + "stop_lon" oftype text, + "zone_id" oftype text, + "stop_url" oftype text + ]; + } + + block TripsTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "route_id" oftype text, + "service_id" oftype text, + "trip_id" oftype text, + "trip_headsign" oftype text, + "direction_id" oftype text, + "block_id" oftype text, + "shape_id" oftype text + ]; + } + + block AgencyLoader oftype SQLiteLoader { + table: "agency"; + file: "./gtfs.sqlite"; + } + + block CalendarDatesLoader oftype SQLiteLoader { + table: "calendar_dates"; + file: "./gtfs.sqlite"; + } + + block CalendarLoader oftype SQLiteLoader { + table: "calendar"; + file: "./gtfs.sqlite"; + } + + block FareAttributesLoader oftype SQLiteLoader { + table: "fare_attributes"; + file: "./gtfs.sqlite"; + } + + block FareRulesLoader oftype SQLiteLoader { + table: "fare_rules"; + file: "./gtfs.sqlite"; + } + + block FrequenciesLoader oftype SQLiteLoader { + table: "frequencies"; + file: "./gtfs.sqlite"; + } + + block RoutesLoader oftype SQLiteLoader { + table: "routes"; + file: "./gtfs.sqlite"; + } + + block ShapesLoader oftype SQLiteLoader { + table: "shapes"; + file: "./gtfs.sqlite"; + } + + block StopTimesLoader oftype SQLiteLoader { + table: "stop_times"; + file: "./gtfs.sqlite"; + } + + block StopsLoader oftype SQLiteLoader { + table: "stops"; + file: "./gtfs.sqlite"; + } + + block TripsLoader oftype SQLiteLoader { + table: "trips"; + file: "./gtfs.sqlite"; + } +} +``` \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.1.0/user/examples/gtfs-static.md.license b/apps/docs/versioned_docs/version-0.1.0/user/examples/gtfs-static.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/examples/gtfs-static.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.1.0/user/examples/workbooks-xlsx.md b/apps/docs/versioned_docs/version-0.1.0/user/examples/workbooks-xlsx.md new file mode 100644 index 00000000..a46e886a --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/examples/workbooks-xlsx.md @@ -0,0 +1,97 @@ +--- +title: workbooks-xlsx +--- + +```jayvee +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// Example 1: LightTrapping +// Learning goals: +// - Understand how to work with XLSX files and workbooks + +// 1. This Jayvee model describes a pipeline +// from a XLSX file with multiple Sheets in the web +// to a SQLite file sink. +pipeline LightTrappingSiliconSolarCellsPipeline { + // 2. We directly get the xlsx file from the web via the HttpExtractor + // The data is provided under CC BY-SA 4.0 + // Saive, Rebecca (2023). Data supporting the publication: + // Light trapping in thin silicon solar cells: a review on fundamentals and technologies. + // 4TU.ResearchData. Dataset. https://doi.org/10.4121/14554815.v1 + block LightTrappingSiliconSolarCellsExtractor oftype HttpExtractor { + url: "https://figshare.com/ndownloader/files/27923598"; + } + + // 3. The incoming file is interpreted as a XLSX file and transformed into a Workbook + // Workbooks contain at least 1 Sheet. Every sheet has a unique name. + block LightTrappingSiliconSolarCellsTextXLSXInterpreter oftype XLSXInterpreter { + + } + + // 4.1 Here, we pick one sheet with the name 'RefractiveIndexSi GaAs' from the Workbook to use within our pipeline. + // The output type from SheetPicker is Sheet, which was already introduced in the cars example + block LightTrappingSiliconSolarCellsSheetpicker oftype SheetPicker { + sheetName: 'RefractiveIndexSi GaAs'; + } + + block NameHeaderWriter oftype CellWriter { + at: range F1:L1; + write: ["F","G","nm","wl","n2", "k2", "alpha (cm-1)2"]; + } + + block LightTrappingSiliconSolarCellsTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "Wavelength" oftype integer, + "Wavelength (µm)" oftype decimal, + "n" oftype decimal, + "k" oftype text, + "alpha (cm-1)" oftype text, + "nm" oftype decimal, + "n2" oftype text, + "k2" oftype decimal, + "alpha (cm-1)2" oftype decimal + ]; + } + + block LightTrappingSiliconSolarCellsLoader oftype SQLiteLoader { + table: "LightTrappingSiliconSolarCells"; + file: "./LightTrappingSiliconSolarCells.sqlite"; + } + + // 4.2 Here, we pick another sheet named 'Wavelength thickness trapping' from the Workbook + block SecondLightTrappingSiliconSolarCellsSheetpicker oftype SheetPicker { + sheetName: 'Wavelength thickness trapping'; + } + + block SecondLightTrappingSiliconSolarCellsTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "n" oftype decimal, + "Wavelength (µm)" oftype decimal, + ]; + } + + block SecondLightTrappingSiliconSolarCellsLoader oftype SQLiteLoader { + + table: "SecondLightTrappingSiliconSolarCells"; + file: "./LightTrappingSiliconSolarCells.sqlite"; + } + + LightTrappingSiliconSolarCellsExtractor + -> LightTrappingSiliconSolarCellsTextXLSXInterpreter + -> LightTrappingSiliconSolarCellsSheetpicker + -> NameHeaderWriter + -> LightTrappingSiliconSolarCellsTableInterpreter + -> LightTrappingSiliconSolarCellsLoader; + + // 5. Once the XLSX file is interpreted, we can split the pipeline and + // work separately on the different sheets from our input file + LightTrappingSiliconSolarCellsTextXLSXInterpreter + -> SecondLightTrappingSiliconSolarCellsSheetpicker + -> SecondLightTrappingSiliconSolarCellsTableInterpreter + -> SecondLightTrappingSiliconSolarCellsLoader; +} +``` \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.1.0/user/examples/workbooks-xlsx.md.license b/apps/docs/versioned_docs/version-0.1.0/user/examples/workbooks-xlsx.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/examples/workbooks-xlsx.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.1.0/user/expressions.md b/apps/docs/versioned_docs/version-0.1.0/user/expressions.md new file mode 100644 index 00000000..99e612a5 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/expressions.md @@ -0,0 +1,80 @@ +--- +sidebar_position: 7 +--- + +# Expressions + +Expressions in Jayvee are arbitrarily nested statements. They consist of: +- literals (e.g., numbers `5` or strings `"Example"`) +- variables (e.g., declared by `from` properties in [Transforms](./transforms.md)) +- operators (e.g., `*` or `sqrt`) + +Expressions get evaluated at runtime by the interpreter to a [Built-in ValueType](./valuetypes/builtin-valuetypes). + +### Example + +The following expression is evaluated to the `integer` `10`: `(2 + 3) * 2` + +The following expression is evaluated to the `integer` `3`: `floor (3.14)` + +The following expression is evaluated to the `boolean` `true`: `"Example" == "Example"` + +### List of Operators + +#### Arithmetics (binary operators) +- `+` for addition, e.g., `5 + 3` evaluates to `8` +- `-` for subtraction, e.g., `5 - 3` evaluates to `2` +- `*` for multiplication, e.g., `5 * 3` evaluates to `15` +- `/` for division, e.g., `6 / 3` evaluates to `2` +- `%` for modulo, e.g., `5 % 3` evaluates to `2` +- `pow` for power, e.g., `2 pow 3` evaluates to `8` +- `root` for root, e.g., `27 root 3` evaluates to `3` + +#### Arithmetics (unary operators) +- `+` for positive signing, e.g., `+5` evaluates to `5` +- `-` for negative signing, e.g., `-5` evaluates to `-5` +- `sqrt` for square root, e.g., `sqrt 9` evaluates to `3` +- `foor` for flooring a number, e.g., `floor 5.3` evaluates to `5` +- `ceil` for ceiling a number, e.g., `floor 5.3` evaluates to `6` +- `round` for rounding a number, e.g., `floor 5.3` evaluates to `5` + +#### Relational (binary operators) +- `<` for smaller, e.g., `3 < 3` evaluates to `false` +- `<=` for smaller or equal, e.g., `3 <= 3` evaluates to `true` +- `>` for greater, e.g., `3 > 3` evaluates to `false` +- `>=` for greater or equal, e.g., `3 >= 3` evaluates to `true` +- `==` for equal, e.g., `3 == 3` evaluates to `true` +- `!=` for not equal, e.g., `3 != 3` evaluates to `false` + +#### Logical (binary operators) +- `and` for a logical and (both need to be true to evaluate to true) +- `or` for a logical or (at least left or right needs to be true to evaluate to true) +- `xor` for a logical xor (either left or right needs to be true to evaluate to true) + +#### Logical (unary operators) +- `not` for logical negation, `not true` evaluates to `false` + +#### Others (binary operators) +- `matches` for a regex match, e.g., `"A07" matches /^[A-Z0-9]*$/` evaluates to `true` +- `in` for inclusion in an array, e.g., `"a" in ["a", "b", "c"]` evaluates to `true` + +### Operator Details + +#### `in` Operator + +The `in` operator checks whether a value is included in a collection of values. For example: + +```jayvee +4.5 in [3, 6.5] // evaluates to false +3 in [3.0, 6.5] // evaluates to true +"a" in ["a", "b", "c"] // evaluates to true +``` + +The operator supports `text`, `integer` and `decimal` values as operands. The compatibility of left and right operand types follows these rules: +- For the `in` operator we have a type for the needle (left operand) and a type for the elements in the haystack (right operand). +- There is an automated type conversion as long as it is lossless and clearly defined (integer to decimal as of now). +- We allow any combination of operands that has either: (i) An automated type conversion from needle type (left operand) to the type of the elements in the haystack (right operand), or (ii) the other way around. + + +### Further reading +For a deeper documentation of how expressions and operators work internally, refer to the [developer docs](../dev/04-guides/04-expressions-and-operators.md). \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.1.0/user/expressions.md.license b/apps/docs/versioned_docs/version-0.1.0/user/expressions.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/expressions.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.1.0/user/intro.md b/apps/docs/versioned_docs/version-0.1.0/user/intro.md new file mode 100644 index 00000000..2832707c --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/intro.md @@ -0,0 +1,95 @@ +--- +sidebar_position: 1 +--- + +# Introduction to Jayvee + +Jayvee is a domain-specific language (DSL) for data engineering - the cleaning and preprocessing of data for later activities like data science or machine learning. You can use Jayvee to **model an ETL pipeline** and the command-line interpreter to **run the ETL pipeline** on your local machine. + +## Installation + +Install the interpreter via `npm`. You will need a **nodejs version >= 17.0.0**. + +```bash +npm install -g @jvalue/jayvee-interpreter +``` + +You can install a specific version using the `@`-syntax, e.g., version `0.0.17`: +```bash +npm install -g @jvalue/jayvee-interpreter@0.0.17 +``` + +## Update + +Updating the interpreter is done by reinstalling it using `npm`. Make sure to also update the [VSCode plugin](#vscode-plugin) to match the installed interpreter if you use it. + +```bash +npm install -g @jvalue/jayvee-interpreter +``` + +## Usage + +### Show help + +```console +jv -h +``` + +### Run a `.jv` file + +```console +jv <file> +``` + +Run with **additional debug output**: + +```console +jv <file> -d +``` + + +With **runtime parameters**: + +```console +jv -e <param>=<value> -e <param>=<value> ... <file> +``` + +### Debug a `.jv` file + +Print debugging is further configured by the parameters `--debug-granularity` and `--debug-target`. + +```console +jv <file> -d -dg peek +``` +The value of the parameter `--debug-granularity` (short `-dg`) can have the following values: +- `peek` to log a short summary, including a small subset of data +- `exhaustive` to log a summary, including the full data +- `minimal` to log a summary, including no additional data (default). +To see logs, debugging has to be enabled using the `-d` flag. + +```console +jv <file> -d --debug-granularity peek +``` +The parameter `--debug-target` (short `-dt`) allows to specify which blocks should be logged for debugging. Separate block names by comma if multiple blocks are targeted. All blocks are logged if the parameter is omitted. +```console +jv <file> -d --debug-granularity peek --debug-target MyExtractorBlock,MySinkBlock +``` + + +## Examples + +You can find multiple examples [here](https://github.com/jvalue/jayvee/tree/main/example). Copy them to your local file system and execute them with the `jv` command on your command line (see [usage](#usage)). + + +## VSCode Plugin + +To set up Jayvee locally in VS Code, you need to install the latest Jayvee VS Code extension. +To install the most recent extension, go to our [latest release](https://github.com/jvalue/jayvee/releases/latest) +and download the `jayvee.vsix` file from the release assets. +Next, go to [this page](https://code.visualstudio.com/docs/editor/extension-marketplace#_install-from-a-vsix) and +follow the instructions for installing the downloaded extension. + +## Troubleshooting + +1. Error `structuredClone is not defined` + * Please make sure you use node version 17+. diff --git a/apps/docs/versioned_docs/version-0.1.0/user/intro.md.license b/apps/docs/versioned_docs/version-0.1.0/user/intro.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/intro.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.1.0/user/runtime-parameters.md b/apps/docs/versioned_docs/version-0.1.0/user/runtime-parameters.md new file mode 100644 index 00000000..bbdab1ed --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/runtime-parameters.md @@ -0,0 +1,27 @@ +--- +sidebar_position: 9 +--- + +# Runtime Parameters + +Property values in Jayvee can be assigned to `values` or left open for later configuration via `runtime parameters`. + +## Syntax + +Runtime parameters are indicated by the `requires` keyword, followed by the identifier of the parameter. Example + +```jayvee +block CarsLoader oftype SQLiteLoader { + table: "Cars"; + file: requires CARS_SQLITE_FILE; +} +``` + +## CLI + +The interpreter CLI has to define all existing runtime parameters for execution. +Use the CLI flag `-e` to to define them as key-value pairs of identifier and value. + +```console +jv -e <param>=<value> -e <param>=<value> ... <file> +``` diff --git a/apps/docs/versioned_docs/version-0.1.0/user/runtime-parameters.md.license b/apps/docs/versioned_docs/version-0.1.0/user/runtime-parameters.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/runtime-parameters.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.1.0/user/transforms.md b/apps/docs/versioned_docs/version-0.1.0/user/transforms.md new file mode 100644 index 00000000..bf3dfd7d --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/transforms.md @@ -0,0 +1,77 @@ +--- +sidebar_position: 8 +--- + +# Transforms + +Transforms are a concept in Jayvee to define the transformation of individual values. +They are similar to functions in programming languages, i.e. they perform computations on some input values and produce output values. Transform work by mapping input values to outputs using [expressions](./expressions.md). + +:::info Important + +Up to version `0.0.16`, we only supported a single input for transformers! + +::: + +:::info Important + +In its current state, Jayvee only supports a arbitrary numbers of inputs and a single output for transforms. +For the future, it is planned to support arbitrary numbers for outputs as well. + +::: + + +## Syntax + +The general syntax of transforms looks like this: + +```jayvee +transform <name> { + from <inputName> oftype <inputValuetype>; + to <outputName> oftype <outputValuetype>; + + <outputName>: <expression>; +} +``` + +The `transform` keyword is used to define a transform and give it a name. +The curly braces denote the body of the transform. + +The body first contains the definitions of input and output ports. +Input ports are defined using the `from` keyword whereas output ports use the `to` keyword. +Next, they are given a name and, after the `oftype` keyword, typed with a valuetype. + +Below, there needs to be an output assignment for each output port. +The output assignment defines how a particular output value is computed. +They consist of the name of an output port, followed by a `:`. +Next, an [expression](./expressions.md) specifies how the output value shall be computed. +Names of input ports can be used in such an expression to refer to input values. + +### Example + +The following transform converts temperature values from degree Celsius to Kelvin: + +```jayvee +transform CelsiusToKelvin { + from tempCelsius oftype decimal; + to tempKelvin oftype decimal; + + tempKelvin: tempCelsius + 273.15; +} +``` + +The following transform converts a text based status into a boolean value, `true` if the text is `Active`, `false` for any other value: + +```jayvee +transform StatusToBoolean { + from statusText oftype text; + to statusBoolean oftype boolean; + + statusBoolean: statusText == "Active"; +} +``` + +## Applying transforms to table columns + +Transforms can be applied to columns of a table. +Please refer to the documentation of the [`TableTransformer` block type](./block-types/TableTransformer.md) to find out how. diff --git a/apps/docs/versioned_docs/version-0.1.0/user/transforms.md.license b/apps/docs/versioned_docs/version-0.1.0/user/transforms.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/transforms.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.1.0/user/valuetypes/_category_.json b/apps/docs/versioned_docs/version-0.1.0/user/valuetypes/_category_.json new file mode 100644 index 00000000..ed1ce95f --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/valuetypes/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Valuetypes", + "position": 5, + "link": { + "type": "generated-index", + "description": "Jayvee supports these different kinds of valuetypes." + } +} diff --git a/apps/docs/versioned_docs/version-0.1.0/user/valuetypes/_category_.json.license b/apps/docs/versioned_docs/version-0.1.0/user/valuetypes/_category_.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/valuetypes/_category_.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.1.0/user/valuetypes/builtin-valuetypes.md b/apps/docs/versioned_docs/version-0.1.0/user/valuetypes/builtin-valuetypes.md new file mode 100644 index 00000000..0bc36dcb --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/valuetypes/builtin-valuetypes.md @@ -0,0 +1,98 @@ +--- +title: Built-in Valuetypes +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +# Description + +For an introduction to valuetypes, see the [Core Concepts](../core-concepts). +Built-in valuetypes come with the basic version of Jayvee. +They are the basis for more restricted [Primitive Valuetypes](./primitive-valuetypes) +that fullfil [Constraints](./primitive-valuetypes#constraints). + +# Available built-in valuetypes + +## Boolean + +### Description + +A boolean value. +Examples: true, false + +### Example 1 + +```jayvee +block ExampleTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "columnName" oftype boolean + ]; +} +``` + +A block of type `TableInterpreter` that + interprets data in the column `columnName` as `boolean`. + +## Decimal + +### Description + +A decimal value. +Example: 3.14 + +### Example 1 + +```jayvee +block ExampleTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "columnName" oftype decimal + ]; +} +``` + +A block of type `TableInterpreter` that + interprets data in the column `columnName` as `decimal`. + +## Integer + +### Description + +An integer value. +Example: 3 + +### Example 1 + +```jayvee +block ExampleTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "columnName" oftype integer + ]; +} +``` + +A block of type `TableInterpreter` that + interprets data in the column `columnName` as `integer`. + +## Text + +### Description + +A text value. +Example: "Hello World" + +### Example 1 + +```jayvee +block ExampleTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "columnName" oftype text + ]; +} +``` + +A block of type `TableInterpreter` that + interprets data in the column `columnName` as `text`. diff --git a/apps/docs/versioned_docs/version-0.1.0/user/valuetypes/builtin-valuetypes.md.license b/apps/docs/versioned_docs/version-0.1.0/user/valuetypes/builtin-valuetypes.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/valuetypes/builtin-valuetypes.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.1.0/user/valuetypes/primitive-valuetypes.md b/apps/docs/versioned_docs/version-0.1.0/user/valuetypes/primitive-valuetypes.md new file mode 100644 index 00000000..92400a53 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/valuetypes/primitive-valuetypes.md @@ -0,0 +1,48 @@ +--- +sidebar_position: 2 +--- +# Primitive ValueTypes + +`Primitive ValueTypes` are based on `Built-in ValueTypes` and use a collection of constraints to restrict the range of valid values. +Such constraints are implicitly connected via a logical `AND` relation. +Note that the `Constraints` need to be applicable to the base-type of the `ValueType` - indicated by the identifier after the keyword `oftype`: + +```jayvee +valuetype GasFillLevel oftype integer { + constraints: [ GasFillLevelRange ]; +} +``` + + +## Constraints + +`Constraints` for `ValueTypes` declare the validity criteria that each concrete value is checked against. + +### Syntax 1: Expression syntax + +The syntax of expression-based `Constraints` uses an expression that evaluates to `true` or `false` for the given `value`. The type of the values the expression is working in is indicated ofter the keyword `on`: + +```jayvee +constraint GasFillLevelRange on decimal: + value >= 0 and value <= 100; +``` + +Refer to the [Expression documentation](../expressions.md) for further reading on expressions. + + +### Syntax 2: Block-like syntax + +The syntax of `Constraints` is similar to the syntax of `Blocks`. +The availability of property keys and their respective `ValueTypes` is determined by the type of the `Constraint` - indicated by the identifier after the keyword `oftype`: + +```jayvee +constraint GasFillLevelRange oftype RangeConstraint { + lowerBound: 0; + lowerBoundInclusive: true; + upperBound: 100; + upperBoundInclusive: true; +} +``` + +Note that the type of `Constraint` also determines its applicability to `ValueTypes`. +For instance, a `RangeConstraint` can only be applied to the numerical types `integer` and `decimal`. \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.1.0/user/valuetypes/primitive-valuetypes.md.license b/apps/docs/versioned_docs/version-0.1.0/user/valuetypes/primitive-valuetypes.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.1.0/user/valuetypes/primitive-valuetypes.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.2.0/dev/01-intro.md b/apps/docs/versioned_docs/version-0.2.0/dev/01-intro.md new file mode 100644 index 00000000..fef3c08e --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/dev/01-intro.md @@ -0,0 +1,52 @@ +--- +sidebar_position: 1 +--- + +# Introduction for Jayvee Developers + +## How to contribute + +In order to contribute to the Jayvee project, please have a look at our [contribution guide](https://github.com/jvalue/jayvee/blob/main/CONTRIBUTING.md). Before planning a contribution, please read the [design principles](./05-design-principles.md) and consider if your changes fit the vision expressed there. + +The overall project is licensed under the `AGPL-3.0-only` license and is compliant to the [REUSE Specification](https://reuse.software/spec/) by the [Free Software Foundation Europe](https://fsfe.org/). +Because of this, contributions are required to adhere to the license and follow that specification. +More details on this topic can be found [here](./02-dev-processes/03-licensing-and-copyright.md). + +And last but not least, please read and follow our [code of conduct](https://github.com/jvalue/jayvee/blob/main/CODE_OF_CONDUCT.md). + +## Project overview + +The Jayvee repository is located [here](https://github.com/jvalue/jayvee) on GitHub. +It uses an [Nx mono-repository](https://nx.dev/) setup and contains all related projects, most notably the Jayvee language server and interpreter. +Please have a look at the [architecture overview](./03-architecture-overview.md) for more details. + +## Prerequisites + +Node.js and npm are required for development. +It is recommended to use their LTS version to avoid any potential compatibility issues. +Also, refer to this [quick start guide](https://github.com/jvalue/jayvee#development-quickstart) for developers. + +In order to run the Jayvee VS Code extension during development, VS Code is required as IDE. +Using VS Code also allows installing the [Langium VS Code extension](https://marketplace.visualstudio.com/items?itemName=langium.langium-vscode) for better IDE support when working with Langium grammar files. + +## Resources for getting started + +### Langium + +- [**Langium documentation**](https://langium.org/docs/) +- Practical examples using Langium: + - [Langium examples](https://github.com/langium/langium/tree/main/examples): Official example languages by Langium + - [Langium's grammar language](https://github.com/langium/langium/tree/main/packages/langium): The implementation of Langium's own grammar language + - [Language server for SQL](https://github.com/langium/langium-sql): An implementation of a language server for SQL + - [MiniLogo DSL](https://github.com/langium/langium-minilogo): A domain-specific language for drawing pictures + - [Lox language](https://github.com/langium/langium-lox): An implementation of the Lox scripting language (also see the "Crafting Interpreters" book below) + - [SimpleUI DSL](https://github.com/TypeFox/langium-ui-framework): A domain-specific language for generating user interfaces + - [STPA-DSL](https://github.com/kieler/stpa): A domain-specific language for System-Theoretic Process Analysis +- [Langium playground](https://langium.org/playground/): A tool for testing Langium grammars and visualizing resulting ASTs +- [Typefox blog](https://www.typefox.io/blog/): A blog by the company behind Langium, mostly posting Langium-related content. + +### Building compilers / interpreters / DSLs + +- [Crafting Interpreters](https://craftinginterpreters.com/contents.html): A book by Robert Nystrom on implementing an interpreter for the Lox scripting language +- [DSL Engineering](https://voelter.de/dslbook/markusvoelter-dslengineering-1.0.pdf): A book by Markus Voelter on designing, implementing and using domain-specific languages +- [Awesome Compilers](https://github.com/aalhour/awesome-compilers#readme): A list featuring many resources on compilers and interpreters diff --git a/apps/docs/versioned_docs/version-0.2.0/dev/01-intro.md.license b/apps/docs/versioned_docs/version-0.2.0/dev/01-intro.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/dev/01-intro.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.2.0/dev/02-dev-processes/01-rfc-process.md b/apps/docs/versioned_docs/version-0.2.0/dev/02-dev-processes/01-rfc-process.md new file mode 100644 index 00000000..07444085 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/dev/02-dev-processes/01-rfc-process.md @@ -0,0 +1,13 @@ +--- +title: Language Design Process (RFCs) +sidebar_position: 1 +--- + +We use RFCs to discuss changes to the language before implementing them. You can have a look at all closed (accepted / rejected) RFCs [here](https://github.com/jvalue/jayvee/pulls?q=is%3Apr+is%3Aclosed+RFC+), and all RFCs under discussion [here](https://github.com/jvalue/jayvee/pulls?q=is%3Apr+is%3Aopen+RFC). + +If you want to contribute an RFC please follow these steps: +1. Make a copy of the **template** at `rfc/0000-rfc-template.md` and place it into the `rfc` folder. +2. Create a draft for the RFC on a new branch. Follow the `TODOs` in template to do so. +3. Open a pull request with prefix `RFC <number>` in the title. +4. Address the reviews. Consider opening a new PR if major things need to be addressed and the discussion log becomes too confusing. +5. Once accepted, create an issue with the necessary steps to implement the RFC. \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.2.0/dev/02-dev-processes/01-rfc-process.md.license b/apps/docs/versioned_docs/version-0.2.0/dev/02-dev-processes/01-rfc-process.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/dev/02-dev-processes/01-rfc-process.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.2.0/dev/02-dev-processes/02-debug-vs-code-extension.md b/apps/docs/versioned_docs/version-0.2.0/dev/02-dev-processes/02-debug-vs-code-extension.md new file mode 100644 index 00000000..5fbcf07c --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/dev/02-dev-processes/02-debug-vs-code-extension.md @@ -0,0 +1,25 @@ +--- +title: Debugging via the VS Code extension +sidebar_position: 2 +--- + +The VS Code extension of Jayvee can be used to interactively debug the language server. +During development, when using VS Code as IDE, another instance of VS Code can be opened which has the most recent, locally built VS Code extension loaded. +This can be achieved using the launch configuration `Run extension` the `Run and Debug` side-menu of VS Code or by pressing the `F5` key if that launch configuration is already selected there. + +Note that there is no file watching mechanism involved. +So in order to reflect changes to the source code, the additional VS Code instance has to be **closed and reopened** as described above. + +## How to attach a debugger + +1. Use the launch configuration `Run extension` to open the additional VS Code instance with the extension loaded +2. Use the launch configuration `Attach to Language Server` to attach the debugger + +Any set breakpoints should now be marked as active and pause the execution once they are reached. + +## How to view logs of the language server + +In the additional VS Code instance, it is possible to view the logs of the language server. +They might also be helpful for debugging since any uncaught errors are logged with their stack trace by the Langium framework. + +To view the logs, open the bottom panel called `Output` and select `Jayvee` in the dropdown menu. diff --git a/apps/docs/versioned_docs/version-0.2.0/dev/02-dev-processes/02-debug-vs-code-extension.md.license b/apps/docs/versioned_docs/version-0.2.0/dev/02-dev-processes/02-debug-vs-code-extension.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/dev/02-dev-processes/02-debug-vs-code-extension.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.2.0/dev/02-dev-processes/03-licensing-and-copyright.md b/apps/docs/versioned_docs/version-0.2.0/dev/02-dev-processes/03-licensing-and-copyright.md new file mode 100644 index 00000000..201e21ca --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/dev/02-dev-processes/03-licensing-and-copyright.md @@ -0,0 +1,69 @@ +--- +title: Licensing and copyright +sidebar_position: 3 +--- + +The [Jayvee repository](https://github.com/jvalue/jayvee) is REUSE compliant, meaning that it fulfills the +[REUSE Specification](https://reuse.software/spec/) by the [Free Software Foundation Europe](https://fsfe.org/) (FSFE). +This makes clear under which license each project file is licensed under and who owns the copyright, not only for +humans but also for machines. + +This is done by explicitly adding copyright and licensing information to each file of the project. This is achieved +by either using a comment header or a separate `*.license` file in case comments are not possible. + +See <https://reuse.software/> more information. + +## What license is used in this project and who is the copyright holder? + +The entire project is licensed under [AGPL-3.0-only](https://spdx.org/licenses/AGPL-3.0-only.html), the +license file can be found [here](https://github.com/jvalue/jayvee/blob/main/LICENSES/AGPL-3.0-only.txt). +The copyright holder is the [Friedrich-Alexander-Universität Erlangen-Nürnberg](https://www.fau.eu/). + +## How to submit a REUSE compliant contribution? + +In case you want to contribute to the project, you will need to ensure that all of your contributed files are REUSE +compliant. In order to achieve this, you need to add a key-value pair for both copyright and licensing information +following this schema: + +``` +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only +``` + +In case a file allows comments, use single-line comments to add the copyright and licensing information at the top +of the file. Otherwise, create a corresponding `*.license` file with the text above as its content. You can have a +look at some existing project files to get an impression on it is done in practice. + +For files with common file extensions, you can use the [reuse CLI tool](https://github.com/fsfe/reuse-tool) to add +licensing and copyright information automatically. + +For more details, you can have a look at the [Getting Started tutorial](https://reuse.software/tutorial/) on the REUSE +website. + +## How to validate REUSE compliance? + +When you make a contribution and open a new pull request, the CI checks whether your contribution is REUSE compliant +using the [reuse CLI tool](https://github.com/fsfe/reuse-tool). + +In order to validate REUSE compliance in your local development environment, you have to install the +[reuse CLI tool](https://github.com/fsfe/reuse-tool) and run the following command in the projects' root folder: + +```bash +reuse lint +``` + +You can also set up a pre-commit hook, so the above command is run before each commit. +See [here](https://reuse.readthedocs.io/en/latest/readme.html#run-as-pre-commit-hook) for details on how to set it up. + +## How to hide `*.license` files in IDEs + +During development, the file explorer of your IDE may be cluttered due to the numerous `*.license` files in the +project. Luckily, most IDEs allow hiding certain files, e.g. by specifying a pattern to exclude them from the +explorer. + +Below, you can find instructions on how to hide `*.license` files in commonly used IDEs: +- **Visual Studio Code**: Add `**/*.license` to the +[`files.exclude` setting](https://code.visualstudio.com/docs/getstarted/userinterface#_explorer) +- **WebStorm**: Add `*.license` to the +[excluded files setting](https://www.jetbrains.com/help/webstorm/configuring-project-structure.html#exclude-by-pattern) diff --git a/apps/docs/versioned_docs/version-0.2.0/dev/02-dev-processes/03-licensing-and-copyright.md.license b/apps/docs/versioned_docs/version-0.2.0/dev/02-dev-processes/03-licensing-and-copyright.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/dev/02-dev-processes/03-licensing-and-copyright.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.2.0/dev/02-dev-processes/04-release-jayvee-version.md b/apps/docs/versioned_docs/version-0.2.0/dev/02-dev-processes/04-release-jayvee-version.md new file mode 100644 index 00000000..374f3468 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/dev/02-dev-processes/04-release-jayvee-version.md @@ -0,0 +1,22 @@ +--- +title: Releasing a new Jayvee version +sidebar_position: 4 +--- + +## Version Numbers + +In this early stage of the project we do not yet follow [semantic versioning](https://semver.org/) since we expect the introduction of breaking changes frequently. +To indicate that, we only release alpha versions where the `version` is incremented with every release. +- For the npm packages, we use the version `0.0.<version>`. +- For the GitHub releases, we use the git tag `v0.0.<version>-alpha`. + +## Jayvee Release Procedure + +For releasing a new version of Jayvee, you need to complete the following steps: + +1. Increment the version in the `package.json` file. +2. Run `npm i` to update the `package-lock.json`. +3. Run `npx nx run docs:version-snapshot` to generate a snapshot of the docs for the previous version. +4. If you are on a feature or dev branch, merge into main. +5. Create a GitHub release on the main branch. Attach a changelog. +6. The CI/CD will deal with the rest. diff --git a/apps/docs/versioned_docs/version-0.2.0/dev/02-dev-processes/04-release-jayvee-version.md.license b/apps/docs/versioned_docs/version-0.2.0/dev/02-dev-processes/04-release-jayvee-version.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/dev/02-dev-processes/04-release-jayvee-version.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.2.0/dev/02-dev-processes/_category_.json b/apps/docs/versioned_docs/version-0.2.0/dev/02-dev-processes/_category_.json new file mode 100644 index 00000000..c8f51245 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/dev/02-dev-processes/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Development Process", + "position": 2, + "link": { + "type": "generated-index", + "description": "Here you can find general processes around developing Jayvee." + } +} diff --git a/apps/docs/versioned_docs/version-0.2.0/dev/02-dev-processes/_category_.json.license b/apps/docs/versioned_docs/version-0.2.0/dev/02-dev-processes/_category_.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/dev/02-dev-processes/_category_.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.2.0/dev/03-architecture-overview.md b/apps/docs/versioned_docs/version-0.2.0/dev/03-architecture-overview.md new file mode 100644 index 00000000..cb4576fa --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/dev/03-architecture-overview.md @@ -0,0 +1,43 @@ +--- +title: Architecture overview +sidebar_position: 4 +--- + +Jayvee has a clear separation between the language itself and its execution. +This guide gives an overview of the overall architecture. + +## Language Server + +On the pure language side, the central project is the [language server](https://github.com/jvalue/jayvee/tree/main/libs/language-server) which is heavily built upon the [Langium framework](https://langium.org/). +It contains the syntax definition (i.e. the grammar) and is capable of performing static semantic analysis on models, so invalid models can be rejected and errors are reported to the user. +It uses the [Language Server Protocol](https://microsoft.github.io/language-server-protocol/) (LSP) for communicating with IDEs in order to provide common features such as diagnostics, auto completion and much more. + +## Interpreter + +The Jayvee [interpreter](https://github.com/jvalue/jayvee/tree/main/apps/interpreter) on the other hand is capable of running Jayvee models. +Therefore, it uses the language server as a library to perform lexing, parsing and semantic analysis on given models. +In case no errors were found during these phases, it executes the model by interpreting it. +This means that models are not compiled to any other language, like a compiler does, but rather directly executed on the fly without any code generation involved. +The interpreter comes with a command-line interface (CLI) for users, so they are able to execute Jayvee models easily. + +The following diagram visualizes the architecture described so far: + +```mermaid +graph LR +model[Jayvee Model] --> lexical(Lexical Analysis) +subgraph Jayvee Interpreter + subgraph Jayvee Language Server + subgraph Langium Framework + lexical --> syntax(Syntax Analysis) + end + syntax --> semantic(Semantic Analysis) + end + semantic --> interpretation(Interpretation) +end +``` + +## Jayvee Extensions + +Lastly, there are Jayvee extensions for adding additional features to Jayvee. +See [here](./04-guides/06-jayvee-extensions.md) for more details. +It is worth pointing out that extensions also follow the separation between language and execution described above. diff --git a/apps/docs/versioned_docs/version-0.2.0/dev/03-architecture-overview.md.license b/apps/docs/versioned_docs/version-0.2.0/dev/03-architecture-overview.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/dev/03-architecture-overview.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.2.0/dev/04-guides/01-jayvee-grammar.md b/apps/docs/versioned_docs/version-0.2.0/dev/04-guides/01-jayvee-grammar.md new file mode 100644 index 00000000..356fb09d --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/dev/04-guides/01-jayvee-grammar.md @@ -0,0 +1,34 @@ +--- +title: The Jayvee grammar +sidebar_position: 1 +--- + +The grammar of Jayvee describes the syntax and structure of the language. +It is located in the language server project. +The grammar itself is written in [Langium's own grammar language](https://langium.org/docs/grammar-language/) which is similar to [Xtext](https://www.eclipse.org/Xtext/) grammars. +Such grammar files are easily identifiable via their `.langium` file extension. + +The grammar is used to generate TypeScript interfaces that represent the Abstract Syntax Tree (AST) and different, semantically equivalent files to define syntax highlighting for different applications. +For instance, a [TextMate](https://macromates.com/manual/en/language_grammars) file is generated for the syntax highlighting in the Jayvee VS Code extension whereas a [Monarch](https://microsoft.github.io/monaco-editor/monarch.html) file is generated for the syntax highlighting in the Monaco editor. + +For further information on the grammar language of Langium, visit the corresponding [Langium documentation](https://langium.org/docs/grammar-language/). + +## Working with the grammar + +To run the code generation, either use `npm run generate` for solely the code generation or `npm run build` for an entire build. The code generation also generates further code, like the standard library. + +Whenever the grammar is changed and the code generation is run during development, it is advisory to **close and reopen the IDE**, so the changes are noticed and the file indexing is updated. + +## How to rename AST nodes + +Renaming AST nodes is not as straight forward as one might initially assume. +When renaming individual rules in the grammar, just the generated TypeScript code in `ast.ts` will reflect the renaming, but not the rest of the codebase. + +The following steps explain how to rename an AST node, so the change is reflected in the entire codebase. As an example, an AST node called `A` is supposed to be renamed to `B`: + +- Open the `ast.ts` file in the language server project +- Locate the `A` interface / type and the `isA` typeguard +- Use the rename feature by the IDE to perform the renaming from `A` to `B` and from `isA` to `isB` +- Open the grammar and locate the `A` rule +- Use the rename feature by the Langium VS Code extension to perform the renaming from `A` to `B` +- Run `npm run generate` diff --git a/apps/docs/versioned_docs/version-0.2.0/dev/04-guides/01-jayvee-grammar.md.license b/apps/docs/versioned_docs/version-0.2.0/dev/04-guides/01-jayvee-grammar.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/dev/04-guides/01-jayvee-grammar.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.2.0/dev/04-guides/02-working-with-the-ast.md b/apps/docs/versioned_docs/version-0.2.0/dev/04-guides/02-working-with-the-ast.md new file mode 100644 index 00000000..ca02dc33 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/dev/04-guides/02-working-with-the-ast.md @@ -0,0 +1,148 @@ +--- +title: Working with the AST +sidebar_position: 2 +--- + +The nodes of the Abstract Syntax Tree (AST) consist of types and interfaces generated from the language grammar. +See [here](./01-jayvee-grammar.md) for more information on that topic. + +The following sections provide practical guides and tips for working with nodes of the AST. + +## Dealing with potentially incomplete AST nodes + +According to the generated interfaces, properties of AST nodes are never `undefined`. +In practice however, this is not always the case. + +For example, consider the language server being confronted with an incomplete Jayvee model with syntax errors. +In such cases, properties of AST nodes are in fact `undefined`, despite their interface definition. +In the interpreter however, after lexical and syntactic analysis succeeded, it can be assumed that all AST nodes are complete (i.e. they have no undefined properties) and even that all references are resolved. + +In order to avoid accessing properties of AST nodes that are potentially undefined, it is recommended to access them via the [`?.` operator](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#optional-chaining) rather than the regular `.` operator. +Note that this might result in a conflict with the ESLint rule [`@typescript-eslint/no-unnecessary-condition`](https://typescript-eslint.io/rules/no-unnecessary-condition/), so it has to be disabled manually for such cases. + +For disabling the rule in an entire file, place this comment below the copyright and licensing header: + +```typescript +/* eslint-disable @typescript-eslint/no-unnecessary-condition */ +``` + +For just a single line, place this comment above that particular line: + +```typescript +// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition +``` + +<details> + +<summary>Full example</summary> + +Consider an exemplary AST node `A` with a property `x` of type `string`. To access that property safely: + +```typescript +import { A } from './ast' + +const astNode: A; + +// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition +const property: string | undefined = astNode?.x; +``` + +</details> + +## Usage of `assertUnreachable` + +Most times, it is beneficial to make case distinctions exhaustive, especially when working with AST nodes, properties with a [union literal type](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types) or enums. +Exhaustiveness in this context means, that the TypeScript compiler is supposed to yield an error if a case distinction does not cover all possibilities. + +Langium offers a function called `assertUnreachable` which is capable of enforcing exhaustiveness and producing compiler errors in case of violations. See the following examples to get an idea on how to use it in practice: + +<details> + +<summary>Example for an exhaustive switch statement on a union literal type</summary> + +```typescript +import { assertUnreachable } from 'langium'; + +const operator: '+' | '-'; + +switch(operator) { + case '+': { + // ... + break; + } + case '-': { + // ... + break; + } + default: { + // To ensure the switch being exhaustive on `operator`: + assertUnreachable(operator); + } +} +``` + +</details> + +<details> + +<summary>Example for an exhaustive if-elseif-else cascade using typeguards</summary> + +Consider the exemplary AST nodes `A`, `B` and `C` and that `A = B | C`: + +```typescript +import { assertUnreachable } from 'langium'; +import { A, B, isB, C, isC } from './ast' + +const astNode: A; + +if (isB(astNode)) { + // `astNode` has type `B` here +} else if (isC(astNode)) { + // `astNode` has type `C` here +} else { + // To ensure the if-elseif-else cascade being exhaustive on `astNode`: + assertUnreachable(astNode); +} +``` + +</details> + +## Usage of `assert` for expressing runtime expectations + +During development, it may occur that a certain condition is expected to **be always true** at runtime. +For example, an AST node being of a certain type or a property being defined. +The type system of TypeScript is not always able to infer such facts, so developers may have to express these expectations explicitly in their code. +Examples are [type assertions](https://www.typescriptlang.org/docs/handbook/advanced-types.html) or the [non-null assertion operator](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html#non-null-assertion-operator). +Their usage may be problematic in case the condition does not always hold, e.g. due to a bug in the program or a wrong expectation by the programmer. +In such cases, it is hard to locate the origin and debug the program because such operations are erased in the compiled JavaScript code. + +To avoid these issues, it is recommended to express such expectations as boolean expressions that are actually validated at runtime. +This can be easily achieved by using the `assert` function. +It evaluates a given boolean expression and throws an `AssertionError` in case it evaluates to `false`. +After calling `assert`, the type system of TypeScript assumes the condition to be `true` and afterwards narrows types accordingly. + +Here is an example of how to use it in practice: + +```typescript +// Import the `assert` function like this: +import { strict as assert } from 'assert'; + +import { A, B, isB } from './ast'; + +const astNode: A; +assert(isB(astNode)); +// Here `astNode` has type `B` + +const referenced = astNode?.reference?.ref; +assert(referenced !== undefined); +// Here `referred` is not `undefined` +``` + +## AST wrapper classes + +The generated interfaces for AST nodes in `ast.ts` are only meant to represent the AST structurally, they don't define any behavior. +Also, in case of syntactic sugar, there may be different kinds of AST nodes representing the same semantic language concept (e.g. single pipes with a verbose syntax or chained pipes). + +To cope with this problem, there is the concept of an `AstNodeWrapper`. +An AST node wrapper is capable of wrapping AST nodes that represent the same semantic language concept and adding behavior to them via custom methods. +To get an impression on how this can be done in practice, please have a look at the [`PipeWrapper` class](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/lib/ast/wrappers/pipe-wrapper.ts) and the [`AstNodeWrapper` interface](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/lib/ast/wrappers/ast-node-wrapper.ts). diff --git a/apps/docs/versioned_docs/version-0.2.0/dev/04-guides/02-working-with-the-ast.md.license b/apps/docs/versioned_docs/version-0.2.0/dev/04-guides/02-working-with-the-ast.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/dev/04-guides/02-working-with-the-ast.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.2.0/dev/04-guides/03-validation-and-diagnostics.md b/apps/docs/versioned_docs/version-0.2.0/dev/04-guides/03-validation-and-diagnostics.md new file mode 100644 index 00000000..328d5728 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/dev/04-guides/03-validation-and-diagnostics.md @@ -0,0 +1,64 @@ +--- +title: Validation and diagnostics +sidebar_position: 3 +--- + +Validation in the context of Jayvee can be understood as semantic analysis of Jayvee models. +The purpose is to uncover errors in models besides lexing, parsing and linking errors that the Langium framework already detects out of the box. +In case such errors are found, the language server makes the IDE display a diagnostic. +This means that the location of the error is underlined and an error message is shown. + +For example, each pipeline is expected to contain at least one starting block that requires no input. +Therefore, the language server analyzes every pipeline in a Jayvee model and checks for the presence of such a starting block. +In case no such block is found, the IDE underlines the pipeline definition in a red color and displays an error message. + +## How to implement validation + +### For arbitrary AST nodes + +In the file [`validation-registry.ts`](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/lib/validation/validation-registry.ts) of the language server, there is a registry containing validation functions for various AST nodes. +A validation function provided for a certain kind of AST node is called for every occurrence of such a node in a Jayvee model. +E.g. when registering a validation function for `PipelineDefinition` nodes, that function gets called once for each pipeline. + +A validation function always operates on a single, concrete AST node. +Besides the AST node, a `ValidationContext` object is passed to the function for reporting diagnostics via its `accept` method. + +The overall validation for a specific kind of AST node is usually subdivided into smaller checks that are called sequentially. +This potentially allows aborting the validation early, i.e. before all checks were run. +Aborting early may be useful if errors were reported during previous checks. +This can be determined via the `ValidationContext` which keeps track whether any errors occurred so far. + +A practical example where this plays a role are blocks: +When the type of a block is unknown to the language server, it makes no sense to validate inputs / outputs and properties of that block. +So, in case an error was reported during the check of the block type, the validation of that block is discontinued at that point. + +### For properties of blocks and constraints + +Validating property values of blocks / constraints works a bit differently because their individual validation is already incorporated into the overall validation framework. + +In general, the validation logic for a property values is located in the meta information of the block type / constraint type. +There, properties are defined using the `PropertySpecification` interface. +In order to add validation logic to a certain property, a validation function needs to be added to that property. + +For more complex validations that need to take multiple property values into account, it is also possible to provide a more general validation function which operates on the entire `PropertyBody` AST node. + +See [`text-range-selector-meta-inf.ts`](https://github.com/jvalue/jayvee/blob/main/libs/extensions/std/lang/src/text-range-selector-meta-inf.ts) for an example which includes both cases described above. + +## Reporting diagnostics + +During validation, diagnostics can be reported by calling the `accept` method of the given `ValidationContext` object. + +The call requires the severity of the diagnostic (`error`, `warning`, `info` or `hint`), the error message and its location. +The location is described by a `DiagnosticInfo` object which contains the affected AST node and optionally some further restrictions. +For more details, have a look at the [`DiagnosticInfo` interface](https://github.com/langium/langium/blob/main/packages/langium/src/validation/validation-registry.ts) by Langium. + +## Common issues + +When working on validations, a diagnostic with the following message may unexpectedly occur: `An error occurred during validation: ...` + +Such a diagnostic is generated by the Langium framework if an error object is thrown within the validation. +In most cases, the reason is the access of an `undefined` AST node property or an `AssertionError`. +For more details on such errors and how to avoid them, have a look at the documentation on [how to work with the AST](./02-working-with-the-ast.md). + +One way locate the origin of such errors is to debug the Jayvee VS Code extension, see [here](../02-dev-processes/02-debug-vs-code-extension.md) for more details. +Another possibility is to debug the interpreter and set appropriate breakpoints in the language server codebase. diff --git a/apps/docs/versioned_docs/version-0.2.0/dev/04-guides/03-validation-and-diagnostics.md.license b/apps/docs/versioned_docs/version-0.2.0/dev/04-guides/03-validation-and-diagnostics.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/dev/04-guides/03-validation-and-diagnostics.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.2.0/dev/04-guides/04-expressions-and-operators.md b/apps/docs/versioned_docs/version-0.2.0/dev/04-guides/04-expressions-and-operators.md new file mode 100644 index 00000000..91b51bd1 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/dev/04-guides/04-expressions-and-operators.md @@ -0,0 +1,151 @@ +--- +title: Expressions and operators +sidebar_position: 4 +--- + +Jayvee supports arbitrarily nested expressions and offers a variety of different operators. +Such expressions are similar to those used in programming languages, and they are intended to be read and understood intuitively. + +The following sections explain the definition of expressions in the language grammar, their type inference mechanism, and how the evaluation of expressions works. +As a small practical example, you may also have a look at the [arithmetics example by Langium](https://github.com/langium/langium/tree/main/examples/arithmetics) which has a heavy focus on mathematical expressions and functions. + +## Expressions in the grammar + +Expressions in the Jayvee grammar are defined in [`expression.langium`](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/grammar/expression.langium) and consist of operators (unary / binary) and literals. +Unary operators only have a single operand (e.g. the `not` operator) whereas binary operators require two operands (e.g. the `*` operator). + +The grammar is written in a way that literals end up in the leaves of the resulting AST and the nodes above represent the operators. + +As an example, have a look at the following AST structure of the expression `(-3 + 5) * 7`: + +```mermaid +classDiagram + +class `:BinaryOperator` { + operator = '*' +} + +%% Uses spaces in the name so it is unique +class ` :BinaryOperator` { + operator = '+' +} + +class `:UnaryOperator` { + operator = '-' +} + +%% Uses spaces in the name so it is unique +class ` :NumericLiteral` { + value = 3 +} + +%% Uses spaces in the name so it is unique +class ` :NumericLiteral` { + value = 5 +} + +class `:NumericLiteral` { + value = 7 +} + +`:BinaryOperator` --> ` :BinaryOperator` : leftOperand +`:BinaryOperator` --> `:NumericLiteral` : rightOperand +` :BinaryOperator` --> `:UnaryOperator` : leftOperand +`:UnaryOperator` --> ` :NumericLiteral` : operand +` :BinaryOperator` --> ` :NumericLiteral` : rightOperand +``` + +The AST is constructed in a way that ensures an unambiguous evaluation order when using depth-first search. +This implies that the AST structure already reflects the precedence and associativity of the operators, and that parentheses do not need to be explicitly represented. + +For more details on how such a grammar for expressions can be realized, see the [official Langium documentation](https://langium.org/docs/grammar-language/#tree-rewriting-actions), [this blog post](https://www.typefox.io/blog/parsing-expressions-with-xtext) and the following two sections which discuss the precedence and associativity of operators. + +### Precedence of operators + +The grammar implicitly defines the precedence of operators. +The operator precedence defines the order in which operators are evaluated within an expression. +For example, multiplication has a higher precedence than addition, which leads to multiplications being performed before additions. +In order to manually override such precedence conventions, parentheses can be used in expressions. + +The diagram below shows a more extensive precedence hierarchy, focused on common arithmetic operators. +Note that the hierarchy is arranged in ascending order, according to the operator precedence. +Such an order is similar to how operators in the actual Jayvee grammar are arranged: + +```mermaid +graph TD + subgraph BinaryOperators + add/sub(Addition Operator / Subtraction operator) --> mul/div(Multiplication Operator / Division Operator) + mul/div --> exp/root(Exponential Operator / Root Operator) + end + subgraph UnaryOperators + exp/root --> plus/minus(Plus Operator / Minus Operator) + end + plus/minus --> literal(Numeric Literal) +``` + +In the Jayvee grammar, every level of precedence is represented by a single grammar rule. +Within each such rule, the grammar rule which defines the operators with the next higher precedence is referenced. +E.g. the `AdditiveExpression` rule refers to the `MultiplicativeExpression` rule because multiplicative operators are supposed to have the next higher precedence. + +In order to alter the precedence of operators, their hierarchy of grammar rules has to be adjusted accordingly in [`expression.langium`](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/grammar/expression.langium). + +### Associativity of operators + +The associativity of operators defines the order in which operators with the same precedence are evaluated when they appear in succession without parentheses. +Operators may be either **left-associative**, **right-associative** or **non-associative**. +For example, depending on the associativity of the binary `+` operator, the expression `a + b + c` has different semantics: + +- Left-associative: `(a + b) + c` (evaluation from left to right) +- Right-associative: `a + (b + c)` (evaluation from right to left) +- Non-associative: _syntax error_, the operator cannot be chained + +The associativity of operators is also defined in the Jayvee grammar, more specifically by the structure of the grammar rules that define operators. +For details on how to encode the different kinds of associativity in grammar rules, have a look at the "Associativity" section near the end of [this blog post](https://www.typefox.io/blog/parsing-expressions-with-xtext). +The patterns described in the linked article can be found in [`expression.langium`](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/grammar/expression.langium), so each operator grammar rule encodes its own associativity. + +## Typing of expressions + +Jayvee has a mechanism for inferring and validating the type of a given expression. +This is achieved using a recursive algorithm which performs depth-first search on a given expression: + +The base case for the algorithm are literals, i.e. the tree leaves of an expression. +Depending on the type of literal, their type can be inferred trivially (in case of value literals) or otherwise from the context where the expression is located. + +For operators, the types of their operands are first inferred via recursion, and then it is checked whether they are supported by the operator. +Next, given the operand types, the resulting type is computed. +Such behavior is defined in a _type computer class_ located [here](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/lib/ast/expressions/type-computers). +Additionally, in [`operator-registry.ts`](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/lib/ast/expressions/operator-registry.ts), a type computer is registered for each kind of operator. + +In case the algorithm fails to infer a type, e.g. due to unsupported operand types or unresolved references in literals, the resulting type is `undefined`. +In order to report diagnostics in such cases, a `ValidationContext` object can be supplied when calling the type inference. + +## Evaluation of expressions + +The evaluation has the purpose of computing a single value out of an expression. +For a successful evaluation, it is a **precondition** that **a type could successfully be inferred** from the respective expression. +Also, the **value for each free variable** in that expression (i.e. literals resembling placeholders for values) needs to be **known beforehand**. +Therefore, an `ExecutionContext` object is passed to the evaluation which holds values for free variables in the current context. + +### Algorithm + +The algorithm for evaluating expressions is also based on a recursive depth-first search, similar to how the type inference works: + +Again, literals are the base case. A value literal trivially evaluates to its own value. +Other literals that resemble free variables evaluate to their value provided by the given `ExecutionContext` object. + +Regarding operators, their operands first are evaluated recursively. +Next, using the concrete operand values, the operator computes its resulting value. +This is defined in operator evaluator classes located [here](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/lib/ast/expressions/evaluators). +The classes are registered in [operator-registry.ts](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/lib/ast/expressions/operator-registry.ts) for every operator, similar to the previously mentioned type computers. + +The result of an evaluation may be `undefined` in case any errors occurred. +If the preconditions were all met, such errors are most likely arithmetic errors (like division by zero). +It is possible to provide a `ValidationContext` object to the evaluation for reporting such errors as diagnostics. + +### Evaluation strategies + +There are different evaluation strategies to choose from. +They affect the way, expressions are evaluated and thus have an impact on their semantics: + +- `exhaustive`: A full depth-first search is performed, all parts of an expression are evaluated. +- `lazy`: An evaluation with the least effort is performed. Logical operators use [short circuit semantics](https://en.wikipedia.org/wiki/Short-circuit_evaluation) and binary operators don't evaluate their right operand if the left one evaluated to `undefined`. diff --git a/apps/docs/versioned_docs/version-0.2.0/dev/04-guides/04-expressions-and-operators.md.license b/apps/docs/versioned_docs/version-0.2.0/dev/04-guides/04-expressions-and-operators.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/dev/04-guides/04-expressions-and-operators.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.2.0/dev/04-guides/05-standard-library.md b/apps/docs/versioned_docs/version-0.2.0/dev/04-guides/05-standard-library.md new file mode 100644 index 00000000..6db17bd7 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/dev/04-guides/05-standard-library.md @@ -0,0 +1,48 @@ +--- +title: Working with the Standard Library +sidebar_position: 5 +--- + +Jayvee ships with its own standard library on board, including the most often used valuetypes, transformations, and so on. +The standard library itself is written in `.jv` files [here](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/stdlib/). + +## Standard Library Contents + +The following elements are part of the standard library: + +## Builtin Contents + +The implementations of builtin contents are not expressed in Jayvee itself but on the TypeScript layer. Examples: + +- **Builtin valuetypes**: These valuetypes are the base for defining user-defined valuetypes in Jayvee, e.g., `text`, `integer`, `decimal`, `boolean`. +- **Builtin iotypes**: These iotypes are used to describe in inputs and outputs of blocktypes, e.g., `Sheet`, `File`. +- **Builtin blocktypes**: These blocktypes are the very basic building blocks in Jayvee, e.g., `HttpExtractor`, `SqliteLoader`. +- **Builtin constraint types**: These constraint types are constraints with custom logic, e.g., `LengthConstraint`, `RegexConstraint`. + +Builtin definitions are usually generated and added to the standard library from the internal representations of the concepts. + +### User-defined Contents + +The implementations of user-defined contents are expressed in Jayvee itself. Examples: + +- **User-defined valuetypes**: These valuetypes are based on builtin or other user-defined valuetypes. Their definition is expressed natively in Jayvee, e.g., `Percent`. +- **User-defined blocktypes**: These blocktypes are based on builtin or other user-defined blocktypes. Their definition is expressed natively in Jayvee. + +We use `jv` files to add user-defined valuetypes to the standard library (see below). + +## Extending the Standard Library + +Just add `jv` files to the directory [here](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/stdlib/). It is crawled hierarchically, meaning that you can also organize files in folders. + +## Implementation + +### 1. Code generation + +We use code generation to transform these `.jv` files into TypeScript files that the language server can used. The [generation script](https://github.com/jvalue/jayvee/tree/main/tools/scripts/language-server/generate-stdlib.mjs) is run via `npm run generate` next to the AST generation. + +### 2. Builtin libraries + +The solution we chose to implement the standard library mechanism is close to the [builtin library tutorial](https://langium.org/guides/builtin-library/) by Langium. The following components are of interest: + +- [JayveeWorkspaceManager](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/lib/builtin-library/jayvee-workspace-manager.ts) in the `language-server` that registers all libraries with the langium framework. +- [StandardLibraryFileSystemProvider](https://github.com/jvalue/jayvee/tree/main/apps/vs-code-extension/src/standard-library-file-system-provider.ts) in the `vs-code-extension` that registers all libraries with the vscode plugin framework. diff --git a/apps/docs/versioned_docs/version-0.2.0/dev/04-guides/05-standard-library.md.license b/apps/docs/versioned_docs/version-0.2.0/dev/04-guides/05-standard-library.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/dev/04-guides/05-standard-library.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.2.0/dev/04-guides/06-jayvee-extensions.md b/apps/docs/versioned_docs/version-0.2.0/dev/04-guides/06-jayvee-extensions.md new file mode 100644 index 00000000..107569d2 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/dev/04-guides/06-jayvee-extensions.md @@ -0,0 +1,190 @@ +--- +title: Jayvee Extensions +sidebar_position: 6 +--- + +## Concepts + +### Jayvee extension + +A Jayvee extension is used to add new block types to the language without having to modify the actual grammar of the language. Such a Jayvee extension usually consists of two parts: a language extension and an execution extension which are described in the upcoming two sections. + +Jayvee extensions that shall be used by default are bundled into the so-called [standard extension](https://github.com/jvalue/jayvee/tree/main/libs/extensions/std). That way, the [language server](https://github.com/jvalue/jayvee/tree/main/libs/language-server) and the [interpreter](https://github.com/jvalue/jayvee/tree/main/apps/interpreter) are able to load them out of the box. + +### Language extension + +A language extension defines meta information of block types which are required by the +[language server](https://github.com/jvalue/jayvee/tree/main/libs/language-server). +Such meta information describes properties of +block types such as their names, input / output types and their properties. + +Note that language extensions don't define any behavior. Instead, this is done by the corresponding execution extension. + +### Execution extension + +An execution extension defines behavior for block types. They build on the meta information from the corresponding +language extension, e.g. input / output types of the block need to match the signature of the execution method and +properties are accessed by their specified name. + +Execution extensions are only required by the [interpreter](https://github.com/jvalue/jayvee/tree/main/apps/interpreter) and not necessarily by the [language server](https://github.com/jvalue/jayvee/tree/main/libs/language-server) as they solely define behavior. + +## Recipes + +### Add a new Jayvee execution extension + +#### 1. Generate an execution libraries + +```bash +npx nx g @nrwl/node:library --name="extensions/<extension-name>/exec" +``` + +#### 2. Create extension classes + +In `libs/extensions/<extension-name>/exec/src/extension.ts`: + +```typescript +import { + BlockExecutorClass, + JayveeExecExtension, +} from '@jvalue/jayvee-execution'; + +export class MyExecExtension implements JayveeExecExtension { + getBlockExecutors(): BlockExecutorClass[] { + return []; + } +} +``` + +#### 3. Export extension classes + +In `libs/extensions/<extension-name>/exec/src/index.ts`: + +```typescript +export * from './extension'; +``` + +#### 4. Register new extension classes in the standard extension + +In `libs/extensions/std/exec/src/extension.ts`: + +```typescript +// ... + +import { MyExecExtension } from '@jvalue/jayvee-extensions/<extension-name>/exec'; + +export class StdExecExtension implements JayveeExecExtension { + private readonly wrappedExtensions: JayveeExecExtension[] = [ + // ... + // Register your execution extension here: + new MyExecExtension(), + // ... + ]; + + // ... +} +``` + +### Add a new block type in a Jayvee extension + +#### 1. Create a builtin blocktype + +Define the syntax of the new blocktype in the [language server's builtin blocktypes](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/stdlib/builtin-blocktypes). + +The following example defines a block type `MyExtractor` with a text property called `url` and a property `retries` with a default value: + +```jayvee +builtin blocktype MyExtractor { + input default oftype None; + output default oftype Sheet; + + property url oftype text; + property retries oftype interger: 10; +} +``` + +The new block type will be automatically registered on the language server startup. + +#### 2. Add custom validation logic (if required) + +If the block type and/or its properties requires custom validation logic, you can implement it in the [language server's block type specific checks](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/lib/validation/checks/blocktype-specific). + +#### 3. Implement `BlockExecutor` + +The following example implements an executor for the previously defined block type `MyExtractor`. + +The `execute` method defines the behavior when a block is executed. Its signature matches the input and output types defined in `MyExtractor.jv` file. + +In `libs/extensions/<extension-name>/exec/src/lib/my-extractor-executor.ts`: + +```typescript +import * as R from '@jvalue/jayvee-execution'; +import { + AbstractBlockExecutor, + BlockExecutorClass, + ExecutionContext, + Sheet, + implementsStatic, +} from '@jvalue/jayvee-execution'; +import { IOType, PrimitiveValuetypes } from '@jvalue/jayvee-language-server'; + +@implementsStatic<BlockExecutorClass>() +export class MyExtractorExecutor + extends AbstractBlockExecutor<IOType.NONE, IOType.SHEET> +{ + // Needs to match the type in meta information: + public static readonly type = 'MyExtractor'; + + public readonly inputType = IOType.NONE; + public readonly outputType = IOType.SHEET; + + async doExecute( + input: None, + context: ExecutionContext, + ): Promise<R.Result<Sheet>> { + // Accessing property values by their name: + const url = context.getPropertyValue( + 'url', + PrimitiveValuetypes.Text, + ); + const limit = context.getPropertyValue( + 'limit', + PrimitiveValuetypes.Integer, + ); + + // ... + + if (error) { + return R.err(...); + } + + return R.ok(...); + } +} +``` + +> **Info** +> The interface `BlockExecutor<I,O>` is used as an API for block executors. The abstract class `AbstractBlockExecutor<I,O>` gives some further functionality for free, e.g., debug logging. + +> **Warning** +> The generic types of `AbstractBlockExecutor<I,O>` need to match the input and output types of the corresponding `blocktype` definition. + +#### 4. Register the new `BlockExecutor` in the execution extension + +In `libs/extensions/<extension-name>/exec/src/extension.ts`: + +```typescript +// ... + +import { MyExtractorExecutor } from './lib/my-extractor-executor'; + +export class MyExecExtension implements JayveeExecExtension { + getBlockExecutors(): BlockExecutorClass[] { + return [ + // ... + // Register your block executor here: + MyExtractorExecutor, + // ... + ]; + } +} +``` diff --git a/apps/docs/versioned_docs/version-0.2.0/dev/04-guides/06-jayvee-extensions.md.license b/apps/docs/versioned_docs/version-0.2.0/dev/04-guides/06-jayvee-extensions.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/dev/04-guides/06-jayvee-extensions.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.2.0/dev/04-guides/_category_.json b/apps/docs/versioned_docs/version-0.2.0/dev/04-guides/_category_.json new file mode 100644 index 00000000..80f6fce8 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/dev/04-guides/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Guides", + "position": 4, + "link": { + "type": "generated-index", + "description": "Here you can find guides that will help you developing certain aspects of Jayvee." + } +} diff --git a/apps/docs/versioned_docs/version-0.2.0/dev/04-guides/_category_.json.license b/apps/docs/versioned_docs/version-0.2.0/dev/04-guides/_category_.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/dev/04-guides/_category_.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.2.0/dev/05-design-principles.md b/apps/docs/versioned_docs/version-0.2.0/dev/05-design-principles.md new file mode 100644 index 00000000..8b15dd8c --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/dev/05-design-principles.md @@ -0,0 +1,24 @@ +--- +title: Design Principles +sidebar_position: 5 +--- + +When deciding on new features for the domain-specific language itself, we try to adhere to the following high level guidelines. Of course, none of these statements is set in stone and every decision is a tradeoff. + +## Jayvee Manifesto +_Inspired by the [Agile Manifesto](https://agilemanifesto.org/)._ + +We are uncovering better ways of _modeling data pipelines by providing a domain-specific language for data engineering and making it easy for everyone to participate in it_. + +Through this work we have come to value: + +1. **Describing a goal state** over how to get there. +2. **Explicit modeling** over hidden magic. +3. **Composition** over inheritance. +4. **Flat structures** over deep nesting. + +That is, while there is value in the items on the right, we value the items on the left more. + +Through this work, we also have come to explore: + +5. **Libraries** over language features. \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.2.0/dev/05-design-principles.md.license b/apps/docs/versioned_docs/version-0.2.0/dev/05-design-principles.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/dev/05-design-principles.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.2.0/dev/12-jayvee-testing.md b/apps/docs/versioned_docs/version-0.2.0/dev/12-jayvee-testing.md new file mode 100644 index 00000000..1121c1ef --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/dev/12-jayvee-testing.md @@ -0,0 +1,254 @@ +--- +title: Writing tests for Jayvee +--- + +In order to ensure that Jayvee works as intended and to catch breaking changes, we have implemented the following components for regression testing: +- Testing utils: utils to create Langium Typescript objects from *.jv test assets (see [here](#testing-utils)) as well as mocks for execution testing (see [here](#testing-utils-1)) +- [Grammar tests](#grammar-tests): test the grammar parsing and validation +- [Execution tests](#execution-tests): test the execution of blocks + +## Conventions +All of the existing tests follow these conventions: +1. The `<file-name>.spec.ts` file is located next to the `<file-name>.ts` file itself. +2. The `*.jv` assets are located inside a `test/assets/<file-name>` folder. +Take a look at one of the exisiting tests for more details. + +## Grammar tests +These kind of tests are mainly located inside the [language-server](https://github.com/jvalue/jayvee/tree/main/libs/language-server) as well as the language parts of each extension (for example [std/lang](https://github.com/jvalue/jayvee/tree/main/libs/extensions/std/lang)). + +### Testing utils +The testing utils are located inside the `language-server` in a dedicated [test folder](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/test). +These utils can be imported using `@jvalue/jayvee-language-server/test` and contain the following parts: + +[**langium-utils.ts**](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/test/langium-utils.ts): +This utils file contains two functions: +- `parseHelper` to simplify parsing the input (content of a *.jv file) and returning the corresponding `LangiumDocument`, and +- `validationHelper` parse and validate the created document. +They are kept in a separate file due to being copied from the Langium repository and thus subject to a different code license and copyright. + +[**utils.ts**](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/test/utils.ts): +This file contains custom testing utility utils functions, like `readJvTestAssetHelper` for reading jv test assets. +Example: +``` ts +import * as path from 'path'; + +import { createJayveeServices } from '@jvalue/jayvee-language-server'; +import { + ParseHelperOptions, + expectNoParserAndLexerErrors, + parseHelper, + readJvTestAssetHelper, +} from '@jvalue/jayvee-language-server/test'; +import { AstNode, LangiumDocument } from 'langium'; + +describe('My example test', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../test/assets/', // localized path to test assets folder + ); + + beforeAll(() => { + // [...] register extensions etc + const services = createJayveeServices(NodeFileSystem).Jayvee; // Or retrieve them if services already exist + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + // [...] + + it('My dummy test', () => { + const text = readJvTestAsset('<sub-folder>/<test-asset-name>.jv'); + + const document = await parse(text); + expectNoParserAndLexerErrors(document); + // Rest of test + }); +}); +``` +If you want to simply validate the test assets, simply replace `parseHelper` with `validationHelper` (and adjust the types). +You can find detailed documentation of all the utility functions directly in the code. + +[**extension/**](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/test/extension): +This folder contains a Jayvee extension for testing. +If there are certain blocks required for testing a certain feature, they can be defined here. +One such example is the already defined `TestProperty` block which has a multitude of different properties, each with a different type. +This block is used for testing properties and property-assignments. +The extension provides loader and extractor blocks for all IOTypes without any properties. +These blocks are automatically generated at runtime with the following naming scheme: +`Test${ioType}${io === 'input' ? 'Loader' : 'Extractor'}` (Example: `TestFileExtractor`). +This allows for easy (grammar) testing of non loader/extractor blocks: +``` jv +pipeline Pipeline { + + TestExtractor -> BlockUnderTest -> TestLoader; + + block BlockUnderTest oftype CellWriter { + at: range A1:A3; + write: ['values', 'to', 'write']; + } + + block TestExtractor oftype TestSheetExtractor { } + block TestLoader oftype TestSheetLoader { } +} +``` + +### Existing tests +Currently there are already tests for the following parts: +- Language-server validation checks (located [here](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/lib/validation)) +- Language-server constraint validation (located [here](https://github.com/jvalue/jayvee/tree/dev/libs/language-server/src/lib/constraint)) +- Custom block (property) validation of the three existing extensions (std extension located [here](https://github.com/jvalue/jayvee/blob/dev/libs/extensions/std/lang/src)) +- Grammar validation tests for all official full examples from the [/example](https://github.com/jvalue/jayvee/tree/main/example) folder (located [here](https://github.com/jvalue/jayvee/blob/dev/libs/extensions/std/lang/src/example-validation.spec.ts)) +- Grammar validation tests for all block examples of the std extension (located [here](https://github.com/jvalue/jayvee/blob/dev/libs/extensions/std/lang/src/meta-inf-example-validation.spec.ts)) + +## Execution tests +These kind of tests are mainly located inside the [interpreter](https://github.com/jvalue/jayvee/tree/main/libs/language-server), the [interpreter-lib](https://github.com/jvalue/jayvee/tree/dev/libs/interpreter-lib), the [execution lib](https://github.com/jvalue/jayvee/tree/dev/libs/execution) as well as the execution parts of each extension (for example [std/exec](https://github.com/jvalue/jayvee/tree/main/libs/extensions/std/exec)). + +### Testing utils +The testing utils for execution tests are spread between the extensions, with the interfaces and base utils located inside the [execution lib](https://github.com/jvalue/jayvee/tree/dev/libs/execution). +They can be imported using `@jvalue/jayvee-extensions/rdbms/test`, `@jvalue/jayvee-extensions/std/test` and `@jvalue/jayvee-execution/test`. + +[**utils.ts**](https://github.com/jvalue/jayvee/blob/dev/libs/execution/test/utils.ts): +At the moment this only contains two functions: +- `clearBlockExecutorRegistry` for clearing the registry containing all `BlockExecutor`s, and +- `clearConstraintExecutorRegistry` clearing the corresponding `ConstraintExecutor`s registry. +They are required in case the tested method initializes Jayvee itself (see [smoke test](#existing-tests-1)). + +[**test-logger.ts**](https://github.com/jvalue/jayvee/blob/dev/libs/execution/test/test-logger.ts): +This contains a subclass of the [`DefaultLogger`](https://github.com/jvalue/jayvee/blob/dev/libs/execution/src/lib/logging/default-logger.ts) used for tests which require a `Logger` implementation. The `TestLogger` contains the following tests functionality: +- `getLogs`: retrieve the cached logs that the logger received. +- `clearLogs`: clear the cached logs. + +[**block-executor-mocks.ts**](https://github.com/jvalue/jayvee/blob/dev/libs/execution/test/block-executor-mock.ts): +`BlockExecutorMock` interface for defining mocks for `AbstractBlockExecutor`. Generally only loader and executor blocks require mocks, because they interact with "the outside world" (i.e. `HttpExtractor` making http calls). +Due to how vastly different each `BlockExecutor` can be, this interface is very simple, containing only a `setup(...args: unknown[])` and a `restore()` method. See below for existing implementations. + +[**rdbms/exec/test**](https://github.com/jvalue/jayvee/tree/dev/libs/extensions/rdbms/exec/test): +Contains the implementation of `BlockExecutorMock` for `PostgresLoaderExecutor` and `SQLiteLoaderExecutor`. +Both of these executors are mocked using `jest.mock` to mock the corresponding libraries (`pg` and `sqlite3`) +**Usage:** +``` ts +import { + PostgresLoaderExecutorMock, + SQLiteLoaderExecutorMock, +} from '@jvalue/jayvee-extensions/rdbms/test'; + +// Global mocking of external library at the top of test file required, +// even though the mocking is encapsulated in helper classes +jest.mock('pg', () => { + const mClient = { + connect: jest.fn(), + query: jest.fn(), + end: jest.fn(), + }; + return { Client: jest.fn(() => mClient) }; +}); +jest.mock('sqlite3', () => { + const mockDB = { + close: jest.fn(), + run: jest.fn(), + }; + return { Database: jest.fn(() => mockDB) }; +}); + +describe('Dummy describe', () => { + // [...] + + let postgresLoaderMock: PostgresLoaderExecutorMock; + let sqliteLoaderMock: SQLiteLoaderExecutorMock; + + beforeAll(() => { + postgresLoaderMock = new PostgresLoaderExecutorMock(); + sqliteLoaderMock = new SQLiteLoaderExecutorMock(); + }); + + afterEach(() => { + postgresLoaderMock.restore(); + sqliteLoaderMock.restore(); + }); + + it('Dummy test', async () => { + // Prepare mocks + postgresLoaderMock.setup(); + sqliteLoaderMock.setup(); + + // [...] execute test + + expect(postgresLoaderMock.pgClient.connect).toBeCalledTimes(1); + expect(postgresLoaderMock.pgClient.query).toBeCalledTimes(1); + expect(postgresLoaderMock.pgClient.end).toBeCalledTimes(1); + expect(sqliteLoaderMock.sqliteClient.run).toBeCalledTimes(1); + expect(sqliteLoaderMock.sqliteClient.close).toBeCalledTimes(1); + }); +}); +``` + +[**std/exec/test/mocks**](https://github.com/jvalue/jayvee/tree/dev/libs/extensions/std/exec/test): +Contains the implementation of `BlockExecutorMock` for `HttpExtractorExecutorMock`. +This implementation uses [nock](https://www.npmjs.com/package/nock) for mocking HTTP(S) responses. +The `setup` method is further specified requiring one parameter `registerMocks: () => Array<nock.Scope>`, which returns all used `nock.Scope` (i.e. the return value of `nock('<URL>')`), see usage below: +**Usage:** +``` ts +import * as path from 'path'; + +import { HttpExtractorExecutorMock } from '@jvalue/jayvee-extensions/std/test'; + +describe('Dummy describe', () => { + // [...] + + let httpExtractorMock: HttpExtractorExecutorMock; + + beforeAll(() => { + httpExtractorMock = new HttpExtractorExecutorMock(); + }); + + afterEach(() => { + httpExtractorMock.restore(); + }); + + it('should have no errors when executing gtfs-static-and-rt.jv example', async () => { + // Prepare mocks + httpExtractorMock.setup(() => { + return [ + nock( + '<URL_1>', + ) + .get('<PATH>') + .replyWithFile( + 200, + path.resolve(__dirname, '../test/assets/file1.zip'), + { + 'Content-Type': 'application/octet-stream', + }, + ), + nock('<URL_2>') + .get('<PATH_1>') + .replyWithFile( + 200, + path.resolve( + __dirname, + '../test/assets/file2', + ), + { + 'Content-Type': 'application/octet-stream', + }, + ) + .get('<PATH_2>') + .reply(200, { content: "My dummy json reply." }), + ]; + }) + + // [...] execute test + + expect(httpExtractorMock.nockScopes.every((scope) => scope.isDone())); + }); +}); +``` + +### Existing tests +Currently there are already tests for the following parts: +- Smoke test for official examples (located [here](https://github.com/jvalue/jayvee/blob/dev/apps/interpreter/src/examples-smoke-test.spec.ts)) diff --git a/apps/docs/versioned_docs/version-0.2.0/dev/12-jayvee-testing.md.license b/apps/docs/versioned_docs/version-0.2.0/dev/12-jayvee-testing.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/dev/12-jayvee-testing.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.2.0/user/block-types/ArchiveInterpreter.md b/apps/docs/versioned_docs/version-0.2.0/user/block-types/ArchiveInterpreter.md new file mode 100644 index 00000000..2a622e27 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/block-types/ArchiveInterpreter.md @@ -0,0 +1,33 @@ +--- +title: ArchiveInterpreter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `File` + +Output type: `FileSystem` + +## Description + +Interprets a `File` as an archive file and converts it to a `FileSystem`. The archive file root is considered the root of the `FileSystem`. + +## Example 1 + +```jayvee + block ZipArchiveInterpreter oftype ArchiveInterpreter { + archiveType: "zip"; + } +``` + +Interprets a `File` as a ZIP-archive and creates a `FileSystem` of its extracted contents. + +## Properties + +### `archiveType` + +Type `text` + +#### Description + +The archive type to be interpreted, e.g., "zip" or "gz". diff --git a/apps/docs/versioned_docs/version-0.2.0/user/block-types/ArchiveInterpreter.md.license b/apps/docs/versioned_docs/version-0.2.0/user/block-types/ArchiveInterpreter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/block-types/ArchiveInterpreter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.2.0/user/block-types/CSVExtractor.md b/apps/docs/versioned_docs/version-0.2.0/user/block-types/CSVExtractor.md new file mode 100644 index 00000000..24cc8acf --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/block-types/CSVExtractor.md @@ -0,0 +1,37 @@ +--- +title: CSVExtractor +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `None` + +Output type: `Sheet` + +## Description + +A CSVExtractor extracts a file from a given URL and interprets it as CSV + +## Properties + +### `url` + +Type `text` + +### `delimiter` + +Type `text` + +Default: `","` + +### `enclosing` + +Type `text` + +Default: `""` + +### `enclosingEscape` + +Type `text` + +Default: `""` diff --git a/apps/docs/versioned_docs/version-0.2.0/user/block-types/CSVExtractor.md.license b/apps/docs/versioned_docs/version-0.2.0/user/block-types/CSVExtractor.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/block-types/CSVExtractor.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.2.0/user/block-types/CSVInterpreter.md b/apps/docs/versioned_docs/version-0.2.0/user/block-types/CSVInterpreter.md new file mode 100644 index 00000000..9242e045 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/block-types/CSVInterpreter.md @@ -0,0 +1,55 @@ +--- +title: CSVInterpreter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `TextFile` + +Output type: `Sheet` + +## Description + +Interprets an input file as a csv-file containing string-values delimited by `delimiter` and outputs a `Sheet`. + +## Example 1 + +```jayvee + block AgencyCSVInterpreter oftype CSVInterpreter { + delimiter: ";"; + } +``` + +Interprets an input file as a csv-file containing string-values delimited by `;` and outputs `Sheet`. + +## Properties + +### `delimiter` + +Type `text` + +Default: `","` + +#### Description + +The delimiter for values in the CSV file. + +### `enclosing` + +Type `text` + +Default: `""` + +#### Description + +The enclosing character that may be used for values in the CSV file. + +### `enclosingEscape` + +Type `text` + +Default: `""` + +#### Description + +The character to escape enclosing characters in values. diff --git a/apps/docs/versioned_docs/version-0.2.0/user/block-types/CSVInterpreter.md.license b/apps/docs/versioned_docs/version-0.2.0/user/block-types/CSVInterpreter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/block-types/CSVInterpreter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.2.0/user/block-types/CellRangeSelector.md b/apps/docs/versioned_docs/version-0.2.0/user/block-types/CellRangeSelector.md new file mode 100644 index 00000000..2e55f64c --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/block-types/CellRangeSelector.md @@ -0,0 +1,33 @@ +--- +title: CellRangeSelector +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Sheet` + +Output type: `Sheet` + +## Description + +Selects a subset of a `Sheet` to produce a new `Sheet`. + +## Example 1 + +```jayvee + block CarsCoreDataSelector oftype CellRangeSelector { + select: range A1:E*; + } +``` + +Selects the cells in the given range and produces a new `Sheet` containing only the selected cells. + +## Properties + +### `select` + +Type `CellRange` + +#### Description + +The cell range to select. diff --git a/apps/docs/versioned_docs/version-0.2.0/user/block-types/CellRangeSelector.md.license b/apps/docs/versioned_docs/version-0.2.0/user/block-types/CellRangeSelector.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/block-types/CellRangeSelector.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.2.0/user/block-types/CellWriter.md b/apps/docs/versioned_docs/version-0.2.0/user/block-types/CellWriter.md new file mode 100644 index 00000000..e397dccf --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/block-types/CellWriter.md @@ -0,0 +1,53 @@ +--- +title: CellWriter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Sheet` + +Output type: `Sheet` + +## Description + +Writes textual values into cells of a `Sheet`. The number of text values needs to match the number of cells to write into. + +## Example 1 + +```jayvee + block NameHeaderWriter oftype CellWriter { + at: cell A1; + write: ["Name"]; + } +``` + +Write the value "Name" into cell `A1`. + +## Example 2 + +```jayvee + block HeaderSequenceWriter oftype CellWriter { + at: range A1:A2; + write: ["Name", "Age"]; + } +``` + +Write the values "Name", "Age" into cells `A1` and `A2`. + +## Properties + +### `write` + +Type `Collection<text>` + +#### Description + +The values to write. + +### `at` + +Type `CellRange` + +#### Description + +The cells to write into. diff --git a/apps/docs/versioned_docs/version-0.2.0/user/block-types/CellWriter.md.license b/apps/docs/versioned_docs/version-0.2.0/user/block-types/CellWriter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/block-types/CellWriter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.2.0/user/block-types/ColumnDeleter.md b/apps/docs/versioned_docs/version-0.2.0/user/block-types/ColumnDeleter.md new file mode 100644 index 00000000..affce276 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/block-types/ColumnDeleter.md @@ -0,0 +1,33 @@ +--- +title: ColumnDeleter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Sheet` + +Output type: `Sheet` + +## Description + +Deletes columns from a `Sheet`. Column IDs of subsequent columns will be shifted accordingly, so there will be no gaps. + +## Example 1 + +```jayvee + block MpgColumnDeleter oftype ColumnDeleter { + delete: [column B]; + } +``` + +Deletes column B (i.e. the second column). + +## Properties + +### `delete` + +Type `Collection<CellRange>` + +#### Description + +The columns to delete. Has to be a full column. diff --git a/apps/docs/versioned_docs/version-0.2.0/user/block-types/ColumnDeleter.md.license b/apps/docs/versioned_docs/version-0.2.0/user/block-types/ColumnDeleter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/block-types/ColumnDeleter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.2.0/user/block-types/FilePicker.md b/apps/docs/versioned_docs/version-0.2.0/user/block-types/FilePicker.md new file mode 100644 index 00000000..9fd0d5ad --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/block-types/FilePicker.md @@ -0,0 +1,33 @@ +--- +title: FilePicker +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `FileSystem` + +Output type: `File` + +## Description + +Selects one `File` from a `FileSystem` based on its relative path to the root of the `FileSystem`. If no file matches the relative path, no output is created and the execution of the pipeline is aborted. + +## Example 1 + +```jayvee + block AgencyFilePicker oftype FilePicker { + path: "./agency.txt"; + } +``` + +Tries to pick the file `agency.txt` from the root of the provided `FileSystem`. If `agency.txt` exists it is passed on as `File`, if it does not exist the execution of the pipeline is aborted. + +## Properties + +### `path` + +Type `text` + +#### Description + +The path of the file to select, relative to the root of the provided `FileSystem`. diff --git a/apps/docs/versioned_docs/version-0.2.0/user/block-types/FilePicker.md.license b/apps/docs/versioned_docs/version-0.2.0/user/block-types/FilePicker.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/block-types/FilePicker.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.2.0/user/block-types/GTFSExtractor.md b/apps/docs/versioned_docs/version-0.2.0/user/block-types/GTFSExtractor.md new file mode 100644 index 00000000..d0d60040 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/block-types/GTFSExtractor.md @@ -0,0 +1,19 @@ +--- +title: GTFSExtractor +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `None` + +Output type: `FileSystem` + +## Description + +A GTFSExtractor extracts a file from a given URL, interprets it as ZIP and extracts it + +## Properties + +### `url` + +Type `text` diff --git a/apps/docs/versioned_docs/version-0.2.0/user/block-types/GTFSExtractor.md.license b/apps/docs/versioned_docs/version-0.2.0/user/block-types/GTFSExtractor.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/block-types/GTFSExtractor.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.2.0/user/block-types/GtfsRTInterpreter.md b/apps/docs/versioned_docs/version-0.2.0/user/block-types/GtfsRTInterpreter.md new file mode 100644 index 00000000..3da451b2 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/block-types/GtfsRTInterpreter.md @@ -0,0 +1,76 @@ +--- +title: GtfsRTInterpreter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `File` + +Output type: `Sheet` + +## Description + +Interprets an protobuf file (binary) of type `File` by decoding the file according to `gtfs-realtime.proto`. Outputs the extracted entity defined by `entity` as a `Sheet` + +## Example 1 + +```jayvee + block GtfsRTTripUpdateInterpreter oftype GtfsRTInterpreter{ + entity: "trip_update"; + } +``` + +A file is interpretet as an GTFS-RT file, which contains TripUpdate. + +## Properties + +### `entity` + +Type `text` + +#### Description + +Entity to process from GTFS-RT-feed (`trip_update`, `alert` or `vehicle`). + We currently support following Output-Sheets, each are an equivalent to the flattened Element Index defined in <https://developers.google.com/transit/gtfs-realtime/reference#element-index> (just required fields are included): + Entity TripUpdate: + ``` + [ + 'header.gtfs_realtime_version', + 'header.timestamp', + 'header.incrementality', + 'entity.id', + 'entity.trip_update.trip.trip_id', + 'entity.trip_update.trip.route_id', + 'entity.trip_update.stop_time_update.stop_sequence', + 'entity.trip_update.stop_time_update.stop_id', + 'entity.trip_update.stop_time_update.arrival.time', + 'entity.trip_update.stop_time_update.departure.time', + ]; + ``` + Entity VehiclePosition: + ``` + [ + 'header.gtfs_realtime_version', + 'header.timestamp', + 'header.incrementality', + 'entity.id', + 'entity.vehicle_position.vehicle_descriptor.id', + 'entity.vehicle_position.trip.trip_id', + 'entity.vehicle_position.trip.route_id', + 'entity.vehicle_position.position.latitude', + 'entity.vehicle_position.position.longitude', + 'entity.vehicle_position.timestamp', + ]; + ``` + Entity Alert: + ``` + [ + 'header.gtfs_realtime_version', + 'header.timestamp', + 'header.incrementality', + 'entity.id', + 'entity.alert.informed_entity.route_id', + 'entity.alert.header_text', + 'entity.alert.description_text', + ]; + ``` diff --git a/apps/docs/versioned_docs/version-0.2.0/user/block-types/GtfsRTInterpreter.md.license b/apps/docs/versioned_docs/version-0.2.0/user/block-types/GtfsRTInterpreter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/block-types/GtfsRTInterpreter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.2.0/user/block-types/HttpExtractor.md b/apps/docs/versioned_docs/version-0.2.0/user/block-types/HttpExtractor.md new file mode 100644 index 00000000..523e4eb3 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/block-types/HttpExtractor.md @@ -0,0 +1,73 @@ +--- +title: HttpExtractor +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `None` + +Output type: `File` + +## Description + +Extracts a `File` from the web. + +## Example 1 + +```jayvee + block CarsFileExtractor oftype HttpExtractor { + url: "tinyurl.com/4ub9spwz"; + } +``` + +Fetches a file from the given URL. + +## Properties + +### `url` + +Type `text` + +#### Description + +The URL to the file in the web to extract. + +### `retries` + +Type `integer` + +Default: `0` + +#### Description + +Configures how many retries should be executed after a failure fetching the data. + +### `retryBackoffMilliseconds` + +Type `integer` + +Default: `1000` + +#### Description + +Configures the wait time in milliseconds before executing a retry. + +### `retryBackoffStrategy` + +Type `text` + +Default: `"exponential"` + +#### Description + +Configures the wait strategy before executing a retry. Can have values "exponential" or "linear". + +### `followRedirects` + +Type `boolean` + +Default: `true` + +#### Description + +Indicates, whether to follow redirects on get requests. If `false`, redirects are not followed. Default `true` diff --git a/apps/docs/versioned_docs/version-0.2.0/user/block-types/HttpExtractor.md.license b/apps/docs/versioned_docs/version-0.2.0/user/block-types/HttpExtractor.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/block-types/HttpExtractor.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.2.0/user/block-types/PostgresLoader.md b/apps/docs/versioned_docs/version-0.2.0/user/block-types/PostgresLoader.md new file mode 100644 index 00000000..91782ab5 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/block-types/PostgresLoader.md @@ -0,0 +1,78 @@ +--- +title: PostgresLoader +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Table` + +Output type: `None` + +## Description + +Loads a `Table` into a PostgreSQL database sink. + +## Example 1 + +```jayvee + block CarsLoader oftype PostgresLoader { + host: "localhost"; + port: 5432; + username: "postgres"; + password: "postgres"; + database: "CarsDB"; + table: "Cars"; + } +``` + +A local Postgres instance is filled with table data about cars. + +## Properties + +### `host` + +Type `text` + +#### Description + +The hostname or IP address of the Postgres database. + +### `port` + +Type `integer` + +#### Description + +The port of the Postgres database. + +### `username` + +Type `text` + +#### Description + +The username to login to the Postgres database. + +### `password` + +Type `text` + +#### Description + +The password to login to the Postgres database. + +### `database` + +Type `text` + +#### Description + +The database to use. + +### `table` + +Type `text` + +#### Description + +The name of the table to write into. diff --git a/apps/docs/versioned_docs/version-0.2.0/user/block-types/PostgresLoader.md.license b/apps/docs/versioned_docs/version-0.2.0/user/block-types/PostgresLoader.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/block-types/PostgresLoader.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.2.0/user/block-types/RowDeleter.md b/apps/docs/versioned_docs/version-0.2.0/user/block-types/RowDeleter.md new file mode 100644 index 00000000..20093b18 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/block-types/RowDeleter.md @@ -0,0 +1,33 @@ +--- +title: RowDeleter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Sheet` + +Output type: `Sheet` + +## Description + +Deletes one or more rows from a `Sheet`. Row IDs of subsequent rows will be shifted accordingly, so there will be no gaps. + +## Example 1 + +```jayvee + block SecondRowDeleter oftype RowDeleter { + delete: [row 2]; + } +``` + +Deletes row 2 (i.e. the second row). + +## Properties + +### `delete` + +Type `Collection<CellRange>` + +#### Description + +The rows to delete. diff --git a/apps/docs/versioned_docs/version-0.2.0/user/block-types/RowDeleter.md.license b/apps/docs/versioned_docs/version-0.2.0/user/block-types/RowDeleter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/block-types/RowDeleter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.2.0/user/block-types/SQLiteLoader.md b/apps/docs/versioned_docs/version-0.2.0/user/block-types/SQLiteLoader.md new file mode 100644 index 00000000..d48e6f0f --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/block-types/SQLiteLoader.md @@ -0,0 +1,52 @@ +--- +title: SQLiteLoader +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Table` + +Output type: `None` + +## Description + +Loads a `Table` into a SQLite database sink. + +## Example 1 + +```jayvee + block CarsLoader oftype SQLiteLoader { + table: "cars"; + file: "./cars.db"; + } +``` + +A SQLite file `cars.db` is created in the working directory. Incoming data is written to the table `cars`. + +## Properties + +### `table` + +Type `text` + +#### Description + +The name of the table to write into. + +### `file` + +Type `text` + +#### Description + +The path to a SQLite file that will be created if it does not exist. Usual file extensions are `.sqlite` and `.db`. + +### `dropTable` + +Type `boolean` + +Default: `true` + +#### Description + +Indicates, whether to drop the table before loading data into it. If `false`, data is appended to the table instead of dropping it. diff --git a/apps/docs/versioned_docs/version-0.2.0/user/block-types/SQLiteLoader.md.license b/apps/docs/versioned_docs/version-0.2.0/user/block-types/SQLiteLoader.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/block-types/SQLiteLoader.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.2.0/user/block-types/SheetPicker.md b/apps/docs/versioned_docs/version-0.2.0/user/block-types/SheetPicker.md new file mode 100644 index 00000000..26710170 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/block-types/SheetPicker.md @@ -0,0 +1,33 @@ +--- +title: SheetPicker +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Workbook` + +Output type: `Sheet` + +## Description + +Selects one `Sheet` from a `Workbook` based on its `sheetName`. If no sheet matches the name, no output is created and the execution of the pipeline is aborted. + +## Example 1 + +```jayvee + block AgencySheetPicker oftype SheetPicker { + sheetName: "AgencyNames"; + } +``` + +Tries to pick the sheet `AgencyNames` from the provided `Workbook`. If `AgencyNames` exists it is passed on as `Sheet`, if it does not exist the execution of the pipeline is aborted. + +## Properties + +### `sheetName` + +Type `text` + +#### Description + +The name of the sheet to select. diff --git a/apps/docs/versioned_docs/version-0.2.0/user/block-types/SheetPicker.md.license b/apps/docs/versioned_docs/version-0.2.0/user/block-types/SheetPicker.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/block-types/SheetPicker.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.2.0/user/block-types/TableInterpreter.md b/apps/docs/versioned_docs/version-0.2.0/user/block-types/TableInterpreter.md new file mode 100644 index 00000000..7d81aae3 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/block-types/TableInterpreter.md @@ -0,0 +1,63 @@ +--- +title: TableInterpreter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Sheet` + +Output type: `Table` + +## Description + +Interprets a `Sheet` as a `Table`. In case a header row is present in the sheet, its names can be matched with the provided column names. Otherwise, the provided column names are assigned in order. + +## Example 1 + +```jayvee + block CarsTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "name" oftype text, + "mpg" oftype decimal, + "cyl" oftype integer, + ]; + } +``` + +Interprets a `Sheet` about cars with a topmost header row and interprets it as a `Table` by assigning a primitive valuetype to each column. The column names are matched to the header, so the order of the type assignments does not matter. + +## Example 2 + +```jayvee + block CarsTableInterpreter oftype TableInterpreter { + header: false; + columns: [ + "name" oftype text, + "mpg" oftype decimal, + "cyl" oftype integer, + ]; + } +``` + +Interprets a `Sheet` about cars without a topmost header row and interprets it as a `Table` by sequentially assigning a name and a primitive valuetype to each column of the sheet. Note that the order of columns matters here. The first column (column `A`) will be named "name", the second column (column `B`) will be named "mpg" etc. + +## Properties + +### `header` + +Type `boolean` + +Default: `true` + +#### Description + +Whether the first row should be interpreted as header row. + +### `columns` + +Type `Collection<ValuetypeAssignment>` + +#### Description + +Collection of valuetype assignments. Uses column names (potentially matched with the header or by sequence depending on the `header` property) to assign a primitive valuetype to each column. diff --git a/apps/docs/versioned_docs/version-0.2.0/user/block-types/TableInterpreter.md.license b/apps/docs/versioned_docs/version-0.2.0/user/block-types/TableInterpreter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/block-types/TableInterpreter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.2.0/user/block-types/TableTransformer.md b/apps/docs/versioned_docs/version-0.2.0/user/block-types/TableTransformer.md new file mode 100644 index 00000000..eb52dc90 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/block-types/TableTransformer.md @@ -0,0 +1,73 @@ +--- +title: TableTransformer +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Table` + +Output type: `Table` + +## Description + +Applies a transform on each value of a column. The input port type of the used transform has to match the type of the input column. + +## Example 1 + +```jayvee + transform CelsiusToFahrenheit { + from Celsius oftype decimal; + to Fahrenheit oftype decimal; + Fahrenheit: (Celsius * 9/5) + 32; + } + block CelsiusToFahrenheitTransformer oftype TableTransformer { + inputColumns: ['temperature']; + outputColumn: 'temperature'; + use: CelsiusToFahrenheit; + } +``` + +Given a column "temperature" with temperature values in Celsius, it overwrites the column with computed values in Fahrenheit by using the `CelsiusToFahrenheit` transform. The transform itself is defined elsewhere in the model. + +## Example 2 + +```jayvee + transform CelsiusToFahrenheit { + from Celsius oftype decimal; + to Fahrenheit oftype decimal; + Fahrenheit: (Celsius * 9/5) + 32; + } + block CelsiusToFahrenheitTransformer oftype TableTransformer { + inputColumns: ['temperatureCelsius']; + outputColumn: 'temperatureFahrenheit'; + use: CelsiusToFahrenheit; + } +``` + +Given a column "temperatureCelsius" with temperature values in Celsius, it adds a new column "temperatureFahrenheit" with computed values in Fahrenheit by using the `CelsiusToFahrenheit` transform. The transform itself is defined elsewhere in the model. + +## Properties + +### `inputColumns` + +Type `Collection<text>` + +#### Description + +The names of the input columns. The columns have to be present in the table and match with the transform's input port types. + +### `outputColumn` + +Type `text` + +#### Description + +The name of the output column. Overwrites the column if it already exists, or otherwise creates a new one. + +### `use` + +Type `Transform` + +#### Description + +Reference to the transform that is applied to the column. diff --git a/apps/docs/versioned_docs/version-0.2.0/user/block-types/TableTransformer.md.license b/apps/docs/versioned_docs/version-0.2.0/user/block-types/TableTransformer.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/block-types/TableTransformer.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.2.0/user/block-types/TextFileInterpreter.md b/apps/docs/versioned_docs/version-0.2.0/user/block-types/TextFileInterpreter.md new file mode 100644 index 00000000..0fefdcc2 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/block-types/TextFileInterpreter.md @@ -0,0 +1,35 @@ +--- +title: TextFileInterpreter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `File` + +Output type: `TextFile` + +## Description + +Interprets a `File` as a `TextFile`. + +## Properties + +### `encoding` + +Type `text` + +Default: `"utf-8"` + +#### Description + +The encoding used for decoding the file contents. + +### `lineBreak` + +Type `Regex` + +Default: `{}` + +#### Description + +The regex for identifying line breaks. diff --git a/apps/docs/versioned_docs/version-0.2.0/user/block-types/TextFileInterpreter.md.license b/apps/docs/versioned_docs/version-0.2.0/user/block-types/TextFileInterpreter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/block-types/TextFileInterpreter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.2.0/user/block-types/TextLineDeleter.md b/apps/docs/versioned_docs/version-0.2.0/user/block-types/TextLineDeleter.md new file mode 100644 index 00000000..b36a46c4 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/block-types/TextLineDeleter.md @@ -0,0 +1,23 @@ +--- +title: TextLineDeleter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `TextFile` + +Output type: `TextFile` + +## Description + +Deletes individual lines from a `TextFile`. + +## Properties + +### `lines` + +Type `Collection<integer>` + +#### Description + +The line numbers to delete. diff --git a/apps/docs/versioned_docs/version-0.2.0/user/block-types/TextLineDeleter.md.license b/apps/docs/versioned_docs/version-0.2.0/user/block-types/TextLineDeleter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/block-types/TextLineDeleter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.2.0/user/block-types/TextRangeSelector.md b/apps/docs/versioned_docs/version-0.2.0/user/block-types/TextRangeSelector.md new file mode 100644 index 00000000..75f5e68e --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/block-types/TextRangeSelector.md @@ -0,0 +1,36 @@ +--- +title: TextRangeSelector +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `TextFile` + +Output type: `TextFile` + +## Description + +Selects a range of lines from a `TextFile`. + +## Properties + +### `lineFrom` + +Type `integer` + +Default: `1` + +#### Description + +Inclusive beginning line number for the selection. + +### `lineTo` + +Type `integer` + +Default: `9007199254740991` + +#### Description + +Inclusive ending line number for the selection. + The default value is the biggest usable integer. diff --git a/apps/docs/versioned_docs/version-0.2.0/user/block-types/TextRangeSelector.md.license b/apps/docs/versioned_docs/version-0.2.0/user/block-types/TextRangeSelector.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/block-types/TextRangeSelector.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.2.0/user/block-types/XLSXInterpreter.md b/apps/docs/versioned_docs/version-0.2.0/user/block-types/XLSXInterpreter.md new file mode 100644 index 00000000..3dd9a3c9 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/block-types/XLSXInterpreter.md @@ -0,0 +1,23 @@ +--- +title: XLSXInterpreter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `File` + +Output type: `Workbook` + +## Description + +Interprets an input file as a XLSX-file and outputs a `Workbook` containing `Sheet`s. + +## Example 1 + +```jayvee + block AgencyXLSXInterpreter oftype XLSXInterpreter { } +``` + +Interprets an input file as a XLSX-file and outputs a `Workbook` containing `Sheet`s. + +## Properties diff --git a/apps/docs/versioned_docs/version-0.2.0/user/block-types/XLSXInterpreter.md.license b/apps/docs/versioned_docs/version-0.2.0/user/block-types/XLSXInterpreter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/block-types/XLSXInterpreter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.2.0/user/block-types/_category_.json b/apps/docs/versioned_docs/version-0.2.0/user/block-types/_category_.json new file mode 100644 index 00000000..740dd2f2 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/block-types/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Block Types", + "position": 3, + "link": { + "type": "generated-index", + "description": "These blocks are shipped with Jayvee and are available right out of the box." + } +} diff --git a/apps/docs/versioned_docs/version-0.2.0/user/block-types/_category_.json.license b/apps/docs/versioned_docs/version-0.2.0/user/block-types/_category_.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/block-types/_category_.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.2.0/user/composite-blocks.md b/apps/docs/versioned_docs/version-0.2.0/user/composite-blocks.md new file mode 100644 index 00000000..f5c9bdb5 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/composite-blocks.md @@ -0,0 +1,115 @@ +--- +sidebar_position: 4 +--- + +# Composite Blocks + +Composite blocks are a way to create new blocktypes in Jayvee by combining the functionality of existing blocks and pipes. By relying on composite blocks instead of implementing more builtin blocks in a language interpreter, Jayvee supports easy extension by users. + +Composite blocks define: +- with the `property` keyword: properties with a name and [value type](./core-concepts.md#valuetypes), optionally a default value +- with the `input` keyword: one input with a name and io type (that can be None) +- with the `output` keyword: one output with a name and io type (that can be None) +- one pipeline definition, starting from the input (using its name) and ending in the output (again using its name) +- all blocks that are used in the pipeline definition (either builtin or other composite blocks) + +## Example +As an example, the common use-case of extracting a CSV file from a webserver using HTTP. With builtin blocks, a pipeline would start with a HttpExtractor source that downloads a file from the internet and outputs a binary file. This file must be interpreted as text (using a TextFileInterpreter) and finally as Sheet (using a CSVInterpreter). + +### Implementation with builtin blocks +```mermaid +flowchart LR + A[HttpExtractor] --> B(TextFileInterpreter) + B --> C(CSVInterpreter) + C --> D(TableInterpreter) + D --> E[SQLiteSink] +``` + +A pipeline with builtin blocks is very verbose: + +```jayvee +pipeline CarsPipeline { + CarsExtractor + -> CarsTextFileInterpreter + -> CarsCSVInterpreter + -> CarsTableInterpreter + -> CarsLoader; + + block CarsExtractor oftype HttpExtractor { + url: "https://example.com/cars.csv"; + } + + block CarsTextFileInterpreter oftype TextFileInterpreter { } + + block CarsCSVInterpreter oftype CSVInterpreter { + enclosing: '"'; + } + // ... further block definitions +} +``` + +### Refactoring using composite blocks + +The common use-case of downloading a CSV file using HTTP can be refactored into a composite block. Note that we define all properties of the builtin blocks that are used as properties of the new CSVExtractor blocktype (but add fallback values). If some internal configuration is always the same, we could also not expose it as a property of the new blocktype. + +```jayvee +// Define a new blocktype named CSVExtractor outside of the pipeline +composite blocktype CSVExtractor { + // Properties of the CSVExtractor, some with default values + property url oftype text; + property delimiter oftype text: ','; + property enclosing oftype text: ''; + property enclosingEscape oftype text: ''; + + // Input and outputs + input inputName oftype None; + output outputName oftype Sheet; + + // Pipeline definition from input, over blocks defined later, to output + inputName + ->FileExtractor + ->FileTextInterpreter + ->FileCSVInterpreter + ->outputName; + + // Block definitions using values from properties by name + block FileExtractor oftype HttpExtractor { url: url; } + block FileTextInterpreter oftype TextFileInterpreter {} + + block FileCSVInterpreter oftype CSVInterpreter { + delimiter: delimiter; + enclosing: enclosing; + enclosingEscape: enclosingEscape; + } +} +``` + +With the new CSVExtractor composite blocktype, the pipeline now looks like this. + +```mermaid +flowchart LR + CSVExtractor --> D(TableInterpreter) + D --> E[SQLiteSink] + + subgraph CSVExtractor + A[HttpExtractor] --> B(TextFileInterpreter) + B --> C(CSVInterpreter) +end +``` + +If the CSVExtractor is available in the scope of the `CarsPipeline` from before (e.g., by defining it above the pipeline), it can then be used to shorten the actual pipeline code. + +```jayvee +pipeline CarsPipeline { + // HttpExtractor, TextFileInterpreter and CSVInterpreter have been replaced by CSVExtractor + CSVExtractor + -> CarsTableInterpreter + -> CarsLoader; + + block CarsExtractor oftype CSVExtractor { + url: "https://example.com/cars.csv"; + } + + // ... further block definitions +} +``` \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.2.0/user/composite-blocks.md.license b/apps/docs/versioned_docs/version-0.2.0/user/composite-blocks.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/composite-blocks.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.2.0/user/constraint-types/AllowlistConstraint.md b/apps/docs/versioned_docs/version-0.2.0/user/constraint-types/AllowlistConstraint.md new file mode 100644 index 00000000..f4710e00 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/constraint-types/AllowlistConstraint.md @@ -0,0 +1,27 @@ +--- +title: AllowlistConstraint +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Compatible ValueType: text + +## Description + +Limits the values to a defined a set of allowed values. Only values in the list are valid. + +## Example 1 + +```jayvee + constraint TimeUnitString oftype AllowlistConstraint { + allowlist: ["ms", "s", "min", "h", "d", "m", "y"]; + } +``` + +Only allows the common abbreviations for millisecond, second, minute, etc.. + +## Properties + +### `allowlist` + +Type `Collection<text>` diff --git a/apps/docs/versioned_docs/version-0.2.0/user/constraint-types/AllowlistConstraint.md.license b/apps/docs/versioned_docs/version-0.2.0/user/constraint-types/AllowlistConstraint.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/constraint-types/AllowlistConstraint.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.2.0/user/constraint-types/DenylistConstraint.md b/apps/docs/versioned_docs/version-0.2.0/user/constraint-types/DenylistConstraint.md new file mode 100644 index 00000000..91a3da32 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/constraint-types/DenylistConstraint.md @@ -0,0 +1,27 @@ +--- +title: DenylistConstraint +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Compatible ValueType: text + +## Description + +Defines a set of forbidden values. All values in the list are considered invalid. + +## Example 1 + +```jayvee + constraint NoPrimaryColors oftype DenylistConstraint { + denylist: ["red", "blue", "yellow"]; + } +``` + +Denies all primary colors. + +## Properties + +### `denylist` + +Type `Collection<text>` diff --git a/apps/docs/versioned_docs/version-0.2.0/user/constraint-types/DenylistConstraint.md.license b/apps/docs/versioned_docs/version-0.2.0/user/constraint-types/DenylistConstraint.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/constraint-types/DenylistConstraint.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.2.0/user/constraint-types/LengthConstraint.md b/apps/docs/versioned_docs/version-0.2.0/user/constraint-types/LengthConstraint.md new file mode 100644 index 00000000..8c4d3729 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/constraint-types/LengthConstraint.md @@ -0,0 +1,37 @@ +--- +title: LengthConstraint +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Compatible ValueType: text + +## Description + +Limits the length of a string with an upper and/or lower boundary. + Only values with a length within the given range are valid. + +## Example 1 + +```jayvee + constraint ShortAnswerConstraint oftype LengthConstraint { + minLength: 0; + maxLength: 20; + } +``` + +A text constraint with 0 to 20 characters. + +## Properties + +### `minLength` + +Type `integer` + +Default: `0` + +### `maxLength` + +Type `integer` + +Default: `9007199254740991` diff --git a/apps/docs/versioned_docs/version-0.2.0/user/constraint-types/LengthConstraint.md.license b/apps/docs/versioned_docs/version-0.2.0/user/constraint-types/LengthConstraint.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/constraint-types/LengthConstraint.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.2.0/user/constraint-types/RangeConstraint.md b/apps/docs/versioned_docs/version-0.2.0/user/constraint-types/RangeConstraint.md new file mode 100644 index 00000000..8b61b1e0 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/constraint-types/RangeConstraint.md @@ -0,0 +1,60 @@ +--- +title: RangeConstraint +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Compatible ValueType: decimal + +## Description + +Limits the range of a number value with an upper and/or lower boundary which can be inclusive or exclusive. Only values within the given range are considered valid. + +## Example 1 + +```jayvee + constraint HundredScale oftype RangeConstraint { + lowerBound: 1; + upperBound: 100; + } +``` + +A scale between (and including) 1 and 100. + +## Example 2 + +```jayvee + constraint HundredScale oftype RangeConstraint { + lowerBound: 1; + lowerBoundInclusive: false; + upperBound: 100; + } +``` + +A scale for numbers strictly larger than 1 and less or equal to 100. + +## Properties + +### `lowerBound` + +Type `decimal` + +Default: `-9007199254740991` + +### `lowerBoundInclusive` + +Type `boolean` + +Default: `true` + +### `upperBound` + +Type `decimal` + +Default: `9007199254740991` + +### `upperBoundInclusive` + +Type `boolean` + +Default: `true` diff --git a/apps/docs/versioned_docs/version-0.2.0/user/constraint-types/RangeConstraint.md.license b/apps/docs/versioned_docs/version-0.2.0/user/constraint-types/RangeConstraint.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/constraint-types/RangeConstraint.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.2.0/user/constraint-types/RegexConstraint.md b/apps/docs/versioned_docs/version-0.2.0/user/constraint-types/RegexConstraint.md new file mode 100644 index 00000000..b9b5c794 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/constraint-types/RegexConstraint.md @@ -0,0 +1,28 @@ +--- +title: RegexConstraint +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Compatible ValueType: text + +## Description + +Limits the values complying with a regex. + Only values that comply with the regex are considered valid. + +## Example 1 + +```jayvee + constraint IPv4Format oftype RegexConstraint { + regex: /^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$/; + } +``` + +Text that complies with the IPv4 address format. + +## Properties + +### `regex` + +Type `Regex` diff --git a/apps/docs/versioned_docs/version-0.2.0/user/constraint-types/RegexConstraint.md.license b/apps/docs/versioned_docs/version-0.2.0/user/constraint-types/RegexConstraint.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/constraint-types/RegexConstraint.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.2.0/user/constraint-types/_category_.json b/apps/docs/versioned_docs/version-0.2.0/user/constraint-types/_category_.json new file mode 100644 index 00000000..cd8b1300 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/constraint-types/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Constraint Types", + "position": 6, + "link": { + "type": "generated-index", + "description": "These constraints are shipped with Jayvee and are available right out of the box." + } +} diff --git a/apps/docs/versioned_docs/version-0.2.0/user/constraint-types/_category_.json.license b/apps/docs/versioned_docs/version-0.2.0/user/constraint-types/_category_.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/constraint-types/_category_.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.2.0/user/core-concepts.md b/apps/docs/versioned_docs/version-0.2.0/user/core-concepts.md new file mode 100644 index 00000000..eba14831 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/core-concepts.md @@ -0,0 +1,111 @@ +--- +sidebar_position: 2 +--- + +# Core Concepts + +The core concepts of Jayvee are `Pipelines`, `Blocks`, and `ValueTypes`. + +## Pipelines + +A `Pipeline` is a sequence of different computing steps, the `Blocks`. +The default output of a block becomes the default input of the next block, building a chain of computing steps. +In the scope of a `Pipeline`, you can connect these blocks via the `pipe` syntax: + +```jayvee +pipeline CarsPipeline { + // Assumption: blocks "GasReserveHttpExtractor", "GasReserveCSVInterpreter", "GasReserveTableInterpreter", and "GasReserveLoader" are defined + + GasReserveHttpExtractor + -> GasReserveTextFileInterpreter + -> GasReserveCSVInterpreter + -> GasReserveTableInterpreter + -> GasReserveLoader; +} +``` + +Alternatively, you can use a slightly longer syntax for pipes: + +```jayvee +pipeline CarsPipeline { + // Assumption: blocks "GasReserveHttpExtractor", "GasReserveCSVInterpreter", "GasReserveTableInterpreter", and "GasReserveLoader" are defined + + pipe { + from: GasReserveHttpExtractor; + to: GasReserveTextFileInterpreter; + + } + + pipe { + from: GasReserveTextFileInterpreter; + to: GasReserveCSVInterpreter; + + } + + // etc. +} +``` + +## Blocks + +A `Block` is a processing step within a `Pipeline`. +It can have a default input and a default output. +We differentiate the following types of `Blocks`: +- `ExtractorBlocks` do not have a default input but only a default output. They model a **data source**. +- `TransformatorBlocks` have a default input and a default output. They model a **transformation**. +- `LoaderBlocks` do have a default input but nor a default output. They model a **data sink**. + +The general structure of a `Pipeline` consisting of different blocks is the following: + +```mermaid +flowchart LR + A[ExtractorBlock] --> B(TransformatorBlock) + B --> C(TransformatorBlock) + C --> D(LoaderBlock) +``` + +The common syntax of blocks is at its core a key-value map to provide configuration to the block. +The availability of property keys and their respective `ValueTypes` is determined by the type of the `Block` - indicated by the identifier after the keyword `oftype`: + +```jayvee +block GasReserveHttpExtractor oftype HttpExtractor { + // key: value + url: "https://www.bundesnetzagentur.de/_tools/SVG/js2/_functions/csv_export.html?view=renderCSV&id=1089590"; +} +``` + +In the example above, the `url` property of type `text` is defined by the corresponding `HttpExtractor` block type. + +Blocks can be either defined as part of the language, called `builtin` or defined as composition of existing blocks by users in Jayvee, called `composite`. See the documentation for [Composite Blocks](./composite-blocks.md). + +## ValueTypes + +A `ValueType` is the definition of a data type of the processed data. +Some `Blocks` use `ValueTypes` to define logic (like filtering or assessing the data type in a data sink). +We differentiate the following types of `ValueTypes`: +- `Built-in ValueTypes` come with the basic version of Jayvee. See [Built-in Valuetypes](./valuetypes/builtin-valuetypes). +- `Primitive ValueTypes` can be defined by the user to model domain-specific data types and represent a single value. + `Constraints` can be added to a `Primitive ValueType`. +See [Primitive Valuetypes](./valuetypes/primitive-valuetypes). +- `Compound ValueTypes`: UPCOMING. + +```jayvee +valuetype GasFillLevel oftype integer { + constraints: [ GasFillLevelRange ]; +} + +constraint GasFillLevelRange on decimal: + value >= 0 and value <= 100; +``` + +## Transforms +`Transforms` are used to transform data from one `ValueType` to a different one. For more details, see [Transforms](./transforms.md). + +```jayvee +transform CelsiusToKelvin { + from tempCelsius oftype decimal; + to tempKelvin oftype decimal; + + tempKelvin: tempCelsius + 273.15; +} +``` \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.2.0/user/core-concepts.md.license b/apps/docs/versioned_docs/version-0.2.0/user/core-concepts.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/core-concepts.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.2.0/user/examples/_category_.json b/apps/docs/versioned_docs/version-0.2.0/user/examples/_category_.json new file mode 100644 index 00000000..65abd42e --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/examples/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Jayvee Examples", + "position": 10, + "link": { + "type": "generated-index", + "description": "Examples of Jayvee models" + } +} diff --git a/apps/docs/versioned_docs/version-0.2.0/user/examples/_category_.json.license b/apps/docs/versioned_docs/version-0.2.0/user/examples/_category_.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/examples/_category_.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.2.0/user/examples/cars.md b/apps/docs/versioned_docs/version-0.2.0/user/examples/cars.md new file mode 100644 index 00000000..e53ede09 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/examples/cars.md @@ -0,0 +1,115 @@ +--- +title: cars +--- + +```jayvee +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// Example 1: Cars +// Learning goals: +// - Understand the core concepts pipeline, block, and pipe +// - Understand the general structure of a pipeline + +// 1. This Jayvee model describes a pipeline +// from a CSV file in the web +// to a SQLite file sink. +pipeline CarsPipeline { + + // 2. We describe the structure of the pipeline, + // usually at the top of the pipeline. + // by connecting blocks via pipes. + + // 3. Verbose syntax of a pipe + // connecting the block CarsExtractor + // with the block CarsTextFileInterpreter. + pipe { + from: CarsExtractor; + to: CarsTextFileInterpreter; + } + + // 4. The output of the "from" block is hereby used + // as input for the "to" block. + + // 5. More convenient syntax of a pipe + CarsTextFileInterpreter -> CarsCSVInterpreter; + + // 6. Pipes can be further chained, + // leading to an overview of the pipeline. + CarsCSVInterpreter + -> NameHeaderWriter + -> CarsTableInterpreter + -> CarsLoader; + + + // 7. Below the pipes, we usually define the blocks + // that are connected by the pipes. + + // 8. Blocks instantiate a blocktype by using the oftype keyword. + // The blocktype defines the available properties that the block + // can use to specify the intended behavior of the block + block CarsExtractor oftype HttpExtractor { + + // 9. Properties are assigned to concrete values. + // Here, we specify the URL where the file shall be downloaded from. + url: "https://gist.githubusercontent.com/noamross/e5d3e859aa0c794be10b/raw/b999fb4425b54c63cab088c0ce2c0d6ce961a563/cars.csv"; + } + + // 10. The HttpExtractor requires no input and produces a binary file as output. + // This file has to be interpreted, e.g., as text file. + block CarsTextFileInterpreter oftype TextFileInterpreter { } + + // 11. Next, we interpret the text file as sheet. + // A sheet only contains text cells and is useful for manipulating the shape of data before assigning more strict value types to cells. + block CarsCSVInterpreter oftype CSVInterpreter { + enclosing: '"'; + } + + // 12. We can write into cells of a sheet using the CellWriter blocktype. + block NameHeaderWriter oftype CellWriter { + // 13. We utilize a syntax similar to spreadsheet programs. + // Cell ranges can be described using the keywords "cell", "row", "column", or "range" that indicate which + // cells are selected for the write action. + at: cell A1; + + // 14. For each cell we selected with the "at" property above, + // we can specify what value shall be written into the cell. + write: ["name"]; + } + + // 15. As a next step, we interpret the sheet as a table by adding structure. + // We define a valuetype per column that specifies the data type of the column. + // Rows that include values that are not valid according to the their valuetypes are dropped automatically. + block CarsTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "name" oftype text, + "mpg" oftype decimal, + "cyl" oftype integer, + "disp" oftype decimal, + "hp" oftype integer, + "drat" oftype decimal, + "wt" oftype decimal, + "qsec" oftype decimal, + "vs" oftype integer, + "am" oftype integer, + "gear" oftype integer, + "carb" oftype integer + ]; + } + + // 16. As a last step, we load the table into a sink, + // here into a sqlite file. + // The structural information of the table is used + // to generate the correct table. + block CarsLoader oftype SQLiteLoader { + table: "Cars"; + file: "./cars.sqlite"; + } + + // 17. Congratulations! + // You can now use the sink for your data analysis, app, + // or whatever you want to do with the cleaned data. +} +``` \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.2.0/user/examples/cars.md.license b/apps/docs/versioned_docs/version-0.2.0/user/examples/cars.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/examples/cars.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.2.0/user/examples/electric-vehicles.md b/apps/docs/versioned_docs/version-0.2.0/user/examples/electric-vehicles.md new file mode 100644 index 00000000..99398633 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/examples/electric-vehicles.md @@ -0,0 +1,150 @@ +--- +title: electric-vehicles +--- + +```jayvee +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// Example 2: Electric Vehicles +// Learning goals: +// - Understand further core concepts transforms and valuetypes +// - Understand how to construct a pipeline with multiple sinks +// - Understand the use of runtime parameters + +// 1. This Jayvee model describes a pipeline +// from a CSV file in the web +// to a SQLite file and a PostgreSQL db sink. +pipeline ElectricVehiclesPipeline { + // See here for meta-data of the data source + // https://catalog.data.gov/dataset/electric-vehicle-population-data/resource/fa51be35-691f-45d2-9f3e-535877965e69 + + // 2. At the top of a pipeline, we describe the + // structure of the pipeline. The first part until + // the ElectricRangeTransformer is a linear sequence + // of blocks. From there we can see a split into two + // parallel sequences that load the data in to two + // different sinks. + ElectricVehiclesHttpExtractor + -> ElectricVehiclesTextFileInterpreter + -> ElectricVehiclesCSVInterpreter + -> ElectricVehiclesTableInterpreter + -> ElectricRangeTransformer; + + ElectricRangeTransformer + -> ElectricVehiclesSQLiteLoader; + + ElectricRangeTransformer + -> ElectricVehiclesPostgresLoader; + + // 3. After the pipeline structure, we define the blocks used. + block ElectricVehiclesHttpExtractor oftype HttpExtractor { + url: "https://data.wa.gov/api/views/f6w7-q2d2/rows.csv?accessType=DOWNLOAD"; + } + + block ElectricVehiclesTextFileInterpreter oftype TextFileInterpreter { } + + block ElectricVehiclesCSVInterpreter oftype CSVInterpreter { } + + block ElectricVehiclesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + // 4. Here, a user-deifned valuetype is used to describe this column. + // The capital letter indicates that the valuetype is not builtin + // by convention. The valuetype itself is defined further below. + "VIN (1-10)" oftype VehicleIdentificationNumber10, + "County" oftype text, + "City" oftype text, + "State" oftype UsStateCode, + "Postal Code" oftype text, + "Model Year" oftype integer, + "Make" oftype text, + "Model" oftype text, + "Electric Vehicle Type" oftype text, + "Clean Alternative Fuel Vehicle (CAFV) Eligibility" oftype text, + "Electric Range" oftype integer, + "Base MSRP" oftype integer, + "Legislative District" oftype text, + "DOL Vehicle ID" oftype integer, + "Vehicle Location" oftype text, + "Electric Utility" oftype text, + "2020 Census Tract" oftype text, + ]; + } + + // 5. This block describes the application of a transform function + // taking a column as input and adding another computed column. + // The applied transform function is defined below and referenced + // by the "use" property. + block ElectricRangeTransformer oftype TableTransformer { + inputColumns: ["Electric Range"]; + outputColumn: "Electric Range (km)"; + use: MilesToKilometers; + } + + // 6. Here, we define a transform function, taking parameters + // as input ("from" keyword), and producing an output ("to" keyword). + // Inputs and outputs have to be further described by a valuetype. + transform MilesToKilometers { + from miles oftype decimal; + to kilometers oftype integer; + + // 7. In order to express what the transform function does, + // we assign an expression to the output. Values from the input and output of the transform can be referred to by name. + kilometers: round (miles * 1.609344); + } + + block ElectricVehiclesSQLiteLoader oftype SQLiteLoader { + table: "ElectricVehiclePopulationData"; + file: "./electric-vehicles.sqlite"; + } + + block ElectricVehiclesPostgresLoader oftype PostgresLoader { + // 8. The requires keyword allows us to define runtime parameters. + // These values have to be provided as environment variables when interpreting the Jayvee model. + host: requires DB_HOST; + port: requires DB_PORT; + username: requires DB_USERNAME; + password: requires DB_PASSWORD; + database: requires DB_DATABASE; + table: "ElectricVehiclePopulationData"; + } +} + +// 9. Below the pipeline, we model user-define valuetypes. +// We give them a speaking name and provide a base valuetype +// that this valuetype builts on. User-defined valuetypes always place additional constraints on existing valuetypes. +valuetype VehicleIdentificationNumber10 oftype text { + // 10. Valuetypes can be further refined by providing constraints. + constraints: [ + OnlyCapitalLettersAndDigits, + ExactlyTenCharacters, + ]; +} + +// 11. This constraint works on text valuetypes and requires values +// to match a given regular expression in order to be valid. +constraint OnlyCapitalLettersAndDigits on text: + value matches /^[A-Z0-9]*$/; + +constraint ExactlyTenCharacters on text: + value.length == 10; + +valuetype UsStateCode oftype text { + constraints: [ + UsStateCodeAllowlist, + ]; +} + +constraint UsStateCodeAllowlist on text: + value in [ + "AL", "AK", "AZ", "AR", "AS", "CA", "CO", "CT", "DE", "DC", + "FL", "GA", "GU", "HI", "ID", "IL", "IN", "IA", "KS", "KY", + "LA", "ME", "MD", "MA", "MI", "MN", "MS", "MO", "MT", "NE", + "NV", "NH", "NJ", "NM", "NY", "NC", "ND", "MP", "OH", "OK", + "OR", "PA", "PR", "RI", "SC", "SD", "TN", "TX", "TT", "UT", + "VT", "VA", "VI", "WA", "WV", "WI", "WY", + ]; + +``` \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.2.0/user/examples/electric-vehicles.md.license b/apps/docs/versioned_docs/version-0.2.0/user/examples/electric-vehicles.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/examples/electric-vehicles.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.2.0/user/examples/gtfs-rt.md b/apps/docs/versioned_docs/version-0.2.0/user/examples/gtfs-rt.md new file mode 100644 index 00000000..7610d8c1 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/examples/gtfs-rt.md @@ -0,0 +1,133 @@ +--- +title: gtfs-rt +--- + +```jayvee +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// Example 3: GTFS Realtime Data +// Learning goals: +// - Understand the construction of a csv file with multiple tables +// - Understand how to work with live data + +// 1. This Jayvee model describes a pipeline +// from a GTFS RT data source in the web +// to a SQLite file with multiple tables. +pipeline GtfsRTSimplePipeline { + + // 2. As you can see here, we have three independent + // sequences of pipes in this pipeline. + GTFSRTTripUpdateFeedExtractor + ->GtfsRTTripUpdateInterpreter + ->TripUpdateTableInterpreter + ->TripUpdateLoader; + + GTFSRTVehiclePositionFeedExtractor + ->GtfsRTVehiclePositionInterpreter + ->VehiclePositionTableInterpreter + ->VehicleLoader; + + GTFSRTAlertFeedExtractor + ->GtfsRTAlertInterpreter + ->AlertTableInterpreter + ->AlertLoader; + + // 3. We define a series of HttpExtractors that each pull data + // from an HTTP endpoint + block GTFSRTTripUpdateFeedExtractor oftype HttpExtractor { + url: "https://proxy.transport.data.gouv.fr/resource/bibus-brest-gtfs-rt-trip-update"; + } + + block GTFSRTVehiclePositionFeedExtractor oftype HttpExtractor { + url: "https://proxy.transport.data.gouv.fr/resource/bibus-brest-gtfs-rt-vehicle-position"; + } + + block GTFSRTAlertFeedExtractor oftype HttpExtractor { + url: "https://proxy.transport.data.gouv.fr/resource/bibus-brest-gtfs-rt-alerts"; + } + + // 4. In the next step, we use the domain-specific GtfsRTInterpreter + // to interpret the fetched files as sheets + block GtfsRTTripUpdateInterpreter oftype GtfsRTInterpreter { + entity: "trip_update"; + } + + block GtfsRTAlertInterpreter oftype GtfsRTInterpreter { + entity: "alert"; + } + + block GtfsRTVehiclePositionInterpreter oftype GtfsRTInterpreter { + entity: "vehicle"; + } + + // 5. Next, we interpret the sheets as tables + block TripUpdateTableInterpreter oftype TableInterpreter { + header: true; + columns:[ + "header.gtfs_realtime_version" oftype text, + "header.timestamp" oftype text, + "header.incrementality" oftype text, + "entity.id" oftype text, + "entity.trip_update.trip.trip_id" oftype text, + "entity.trip_update.trip.route_id" oftype text, + "entity.trip_update.stop_time_update.stop_sequence" oftype text, + "entity.trip_update.stop_time_update.stop_id" oftype text, + "entity.trip_update.stop_time_update.arrival.time" oftype text, + "entity.trip_update.stop_time_update.departure.time" oftype text, + ]; + } + + block VehiclePositionTableInterpreter oftype TableInterpreter { + header: true; + columns:[ + "header.gtfs_realtime_version" oftype text, + "header.timestamp" oftype text, + "header.incrementality" oftype text, + "entity.id" oftype text, + "entity.vehicle_position.vehicle_descriptor.id" oftype text, + "entity.vehicle_position.trip.trip_id" oftype text, + "entity.vehicle_position.trip.route_id" oftype text, + "entity.vehicle_position.position.latitude" oftype text, + "entity.vehicle_position.position.longitude" oftype text, + "entity.vehicle_position.timestamp" oftype text + ]; + } + + block AlertTableInterpreter oftype TableInterpreter { + header: true; + columns:[ + 'header.gtfs_realtime_version' oftype text, + 'header.timestamp' oftype text, + 'header.incrementality' oftype text, + 'entity.id' oftype text, + 'entity.alert.informed_entity.route_id' oftype text, + 'entity.alert.header_text' oftype text, + 'entity.alert.description_text' oftype text, + ]; + } + + // 6. Last, we load the tables into the same SQLite file. + // Each loader has to define a different table name. + // For working with live data, we use the property "dropTable: false" + // to append data instead of deleting the previous data. + block TripUpdateLoader oftype SQLiteLoader { + table: "gtfs-rt-trip_update"; + file: "./gtfs.sqlite"; + dropTable: false; + } + + block VehicleLoader oftype SQLiteLoader { + table: "gtfs-rt-vehicle_position"; + file: "./gtfs.sqlite"; + dropTable: false; + } + + block AlertLoader oftype SQLiteLoader { + table: "gtfs-rt-alert"; + file: "./gtfs.sqlite"; + dropTable: false; + } +} +``` \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.2.0/user/examples/gtfs-rt.md.license b/apps/docs/versioned_docs/version-0.2.0/user/examples/gtfs-rt.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/examples/gtfs-rt.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.2.0/user/examples/gtfs-static.md b/apps/docs/versioned_docs/version-0.2.0/user/examples/gtfs-static.md new file mode 100644 index 00000000..ca6434df --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/examples/gtfs-static.md @@ -0,0 +1,370 @@ +--- +title: gtfs-static +--- + +```jayvee +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// Example 4: GTFS Static Data +// Learning goals: +// - Understand how to work with file systems + +// 1. This Jayvee model describes a pipeline +// from a zip file in the GTFS format in the web +// to a joint SQLite file with multiple tables. +pipeline GtfsPipeline { + + // 2. The origin for multiple pipe sequences is a zip + // file. Each csv file in this zip is further processed + // by its own sequence of blocks and pipes. + GTFSSampleFeedExtractor -> ZipArchiveInterpreter; + + ZipArchiveInterpreter + -> AgencyFilePicker + -> AgencyTextFileInterpreter + -> AgencyCSVInterpreter + -> AgencyTableInterpreter + -> AgencyLoader; + + ZipArchiveInterpreter + -> CalendarDatesFilePicker + -> CalendarDatesTextFileInterpreter + -> CalendarDatesCSVInterpreter + -> CalendarDatesTableInterpreter + -> CalendarDatesLoader; + + ZipArchiveInterpreter + -> CalendarFilePicker + -> CalendarTextFileInterpreter + -> CalendarCSVInterpreter + -> CalendarTableInterpreter + -> CalendarLoader; + + ZipArchiveInterpreter + -> FareAttributesFilePicker + -> FareAttributesTextFileInterpreter + -> FareAttributesCSVInterpreter + -> FareAttributesTableInterpreter + -> FareAttributesLoader; + + ZipArchiveInterpreter + -> FareRulesFilePicker + -> FareRulesTextFileInterpreter + -> FareRulesCSVInterpreter + -> FareRulesTableInterpreter + -> FareRulesLoader; + + ZipArchiveInterpreter + -> FrequenciesFilePicker + -> FrequenciesTextFileInterpreter + -> FrequenciesCSVInterpreter + -> FrequenciesTableInterpreter + -> FrequenciesLoader; + + ZipArchiveInterpreter + -> RoutesFilePicker + -> RoutesTextFileInterpreter + -> RoutesCSVInterpreter + -> RoutesTableInterpreter + -> RoutesLoader; + + ZipArchiveInterpreter + -> ShapesFilePicker + -> ShapesTextFileInterpreter + -> ShapesCSVInterpreter + -> ShapesTableInterpreter + -> ShapesLoader; + + ZipArchiveInterpreter + -> StopTimesFilePicker + -> StopTimesTextFileInterpreter + -> StopTimesCSVInterpreter + -> StopTimesTableInterpreter + -> StopTimesLoader; + + ZipArchiveInterpreter + -> StopsFilePicker + -> StopsTextFileInterpreter + -> StopsCSVInterpreter + -> StopsTableInterpreter + -> StopsLoader; + + ZipArchiveInterpreter + -> TripsFilePicker + -> TripsTextFileInterpreter + -> TripsCSVInterpreter + -> TripsTableInterpreter + -> TripsLoader; + + // 3. As a first step, we download the zip file and interpret it. + block GTFSSampleFeedExtractor oftype HttpExtractor { + url: "https://developers.google.com/static/transit/gtfs/examples/sample-feed.zip"; + } + + block ZipArchiveInterpreter oftype ArchiveInterpreter { + archiveType: "zip"; + } + + // 4. Next, we pick several csv files (with the file extension ".txt") + // for further processing . + block AgencyFilePicker oftype FilePicker { + path: "/agency.txt"; + } + + block CalendarDatesFilePicker oftype FilePicker { + path: "/calendar_dates.txt"; + } + + block CalendarFilePicker oftype FilePicker { + path: "/calendar.txt"; + } + + block FareAttributesFilePicker oftype FilePicker { + path: "/fare_attributes.txt"; + } + + block FareRulesFilePicker oftype FilePicker { + path: "/fare_rules.txt"; + } + + block FrequenciesFilePicker oftype FilePicker { + path: "/frequencies.txt"; + } + + block RoutesFilePicker oftype FilePicker { + path: "/routes.txt"; + } + + block ShapesFilePicker oftype FilePicker { + path: "/shapes.txt"; + } + + block StopTimesFilePicker oftype FilePicker { + path: "/stop_times.txt"; + } + + block StopsFilePicker oftype FilePicker { + path: "/stops.txt"; + } + + block TripsFilePicker oftype FilePicker { + path: "/trips.txt"; + } + + // 5. The rest of the pipeline follows the usual pattern. + block AgencyTextFileInterpreter oftype TextFileInterpreter { } + block CalendarDatesTextFileInterpreter oftype TextFileInterpreter { } + block CalendarTextFileInterpreter oftype TextFileInterpreter { } + block FareAttributesTextFileInterpreter oftype TextFileInterpreter { } + block FareRulesTextFileInterpreter oftype TextFileInterpreter { } + block FrequenciesTextFileInterpreter oftype TextFileInterpreter { } + block RoutesTextFileInterpreter oftype TextFileInterpreter { } + block ShapesTextFileInterpreter oftype TextFileInterpreter { } + block StopTimesTextFileInterpreter oftype TextFileInterpreter { } + block StopsTextFileInterpreter oftype TextFileInterpreter { } + block TripsTextFileInterpreter oftype TextFileInterpreter { } + block AgencyCSVInterpreter oftype CSVInterpreter { } + block CalendarDatesCSVInterpreter oftype CSVInterpreter { } + block CalendarCSVInterpreter oftype CSVInterpreter { } + block FareAttributesCSVInterpreter oftype CSVInterpreter { } + block FareRulesCSVInterpreter oftype CSVInterpreter { } + block FrequenciesCSVInterpreter oftype CSVInterpreter { } + block RoutesCSVInterpreter oftype CSVInterpreter { } + block ShapesCSVInterpreter oftype CSVInterpreter { } + block StopTimesCSVInterpreter oftype CSVInterpreter { } + block StopsCSVInterpreter oftype CSVInterpreter { } + block TripsCSVInterpreter oftype CSVInterpreter { } + + block AgencyTableInterpreter oftype TableInterpreter { + header: true; + columns:[ + "agency_id" oftype text, //Conditional columns are considered as required + "agency_name" oftype text, + "agency_url" oftype text, + "agency_timezone" oftype text + ]; + } + + block CalendarDatesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "service_id" oftype text, + "date" oftype text, + "exception_type" oftype text + ]; + } + + block CalendarTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "service_id" oftype text, + "monday" oftype text, + "tuesday" oftype text, + "wednesday" oftype text, + "thursday" oftype text, + "friday" oftype text, + "saturday" oftype text, + "sunday" oftype text, + "start_date" oftype text, + "end_date" oftype text + ]; + } + + block FareAttributesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "fare_id" oftype text, + "price" oftype text, + "currency_type" oftype text, + "payment_method" oftype text, + "transfers" oftype text, + "transfer_duration" oftype text + ]; + } + + block FareRulesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "fare_id" oftype text, + "route_id" oftype text, + "origin_id" oftype text, + "destination_id" oftype text, + "contains_id" oftype text + ]; + } + + block FrequenciesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "trip_id" oftype text, + "start_time" oftype text, + "end_time" oftype text, + "headway_secs" oftype text + ]; + } + + block RoutesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "route_id" oftype text, + "agency_id" oftype text, + "route_short_name" oftype text, + "route_long_name" oftype text, + "route_desc" oftype text, + "route_type" oftype text, + "route_url" oftype text, + "route_color" oftype text, + "route_text_color" oftype text + ]; + } + + block ShapesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "shape_id" oftype text, + "shape_pt_lat" oftype text, + "shape_pt_lon" oftype text, + "shape_pt_sequence" oftype text, + "shape_dist_traveled" oftype text + ]; + } + + block StopTimesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "trip_id" oftype text, + "arrival_time" oftype text, + "departure_time" oftype text, + "stop_id" oftype text, + "stop_sequence" oftype text, + "stop_headsign" oftype text, + "pickup_type" oftype text, + "drop_off_time" oftype text, + "shape_dist_traveled" oftype text + ]; + } + + block StopsTableInterpreter oftype TableInterpreter { + header: true; + columns:[ + "stop_id" oftype text, + "stop_name" oftype text, + "stop_desc" oftype text, + "stop_lat" oftype text, + "stop_lon" oftype text, + "zone_id" oftype text, + "stop_url" oftype text + ]; + } + + block TripsTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "route_id" oftype text, + "service_id" oftype text, + "trip_id" oftype text, + "trip_headsign" oftype text, + "direction_id" oftype text, + "block_id" oftype text, + "shape_id" oftype text + ]; + } + + block AgencyLoader oftype SQLiteLoader { + table: "agency"; + file: "./gtfs.sqlite"; + } + + block CalendarDatesLoader oftype SQLiteLoader { + table: "calendar_dates"; + file: "./gtfs.sqlite"; + } + + block CalendarLoader oftype SQLiteLoader { + table: "calendar"; + file: "./gtfs.sqlite"; + } + + block FareAttributesLoader oftype SQLiteLoader { + table: "fare_attributes"; + file: "./gtfs.sqlite"; + } + + block FareRulesLoader oftype SQLiteLoader { + table: "fare_rules"; + file: "./gtfs.sqlite"; + } + + block FrequenciesLoader oftype SQLiteLoader { + table: "frequencies"; + file: "./gtfs.sqlite"; + } + + block RoutesLoader oftype SQLiteLoader { + table: "routes"; + file: "./gtfs.sqlite"; + } + + block ShapesLoader oftype SQLiteLoader { + table: "shapes"; + file: "./gtfs.sqlite"; + } + + block StopTimesLoader oftype SQLiteLoader { + table: "stop_times"; + file: "./gtfs.sqlite"; + } + + block StopsLoader oftype SQLiteLoader { + table: "stops"; + file: "./gtfs.sqlite"; + } + + block TripsLoader oftype SQLiteLoader { + table: "trips"; + file: "./gtfs.sqlite"; + } +} +``` \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.2.0/user/examples/gtfs-static.md.license b/apps/docs/versioned_docs/version-0.2.0/user/examples/gtfs-static.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/examples/gtfs-static.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.2.0/user/examples/workbooks-xlsx.md b/apps/docs/versioned_docs/version-0.2.0/user/examples/workbooks-xlsx.md new file mode 100644 index 00000000..a46e886a --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/examples/workbooks-xlsx.md @@ -0,0 +1,97 @@ +--- +title: workbooks-xlsx +--- + +```jayvee +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// Example 1: LightTrapping +// Learning goals: +// - Understand how to work with XLSX files and workbooks + +// 1. This Jayvee model describes a pipeline +// from a XLSX file with multiple Sheets in the web +// to a SQLite file sink. +pipeline LightTrappingSiliconSolarCellsPipeline { + // 2. We directly get the xlsx file from the web via the HttpExtractor + // The data is provided under CC BY-SA 4.0 + // Saive, Rebecca (2023). Data supporting the publication: + // Light trapping in thin silicon solar cells: a review on fundamentals and technologies. + // 4TU.ResearchData. Dataset. https://doi.org/10.4121/14554815.v1 + block LightTrappingSiliconSolarCellsExtractor oftype HttpExtractor { + url: "https://figshare.com/ndownloader/files/27923598"; + } + + // 3. The incoming file is interpreted as a XLSX file and transformed into a Workbook + // Workbooks contain at least 1 Sheet. Every sheet has a unique name. + block LightTrappingSiliconSolarCellsTextXLSXInterpreter oftype XLSXInterpreter { + + } + + // 4.1 Here, we pick one sheet with the name 'RefractiveIndexSi GaAs' from the Workbook to use within our pipeline. + // The output type from SheetPicker is Sheet, which was already introduced in the cars example + block LightTrappingSiliconSolarCellsSheetpicker oftype SheetPicker { + sheetName: 'RefractiveIndexSi GaAs'; + } + + block NameHeaderWriter oftype CellWriter { + at: range F1:L1; + write: ["F","G","nm","wl","n2", "k2", "alpha (cm-1)2"]; + } + + block LightTrappingSiliconSolarCellsTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "Wavelength" oftype integer, + "Wavelength (µm)" oftype decimal, + "n" oftype decimal, + "k" oftype text, + "alpha (cm-1)" oftype text, + "nm" oftype decimal, + "n2" oftype text, + "k2" oftype decimal, + "alpha (cm-1)2" oftype decimal + ]; + } + + block LightTrappingSiliconSolarCellsLoader oftype SQLiteLoader { + table: "LightTrappingSiliconSolarCells"; + file: "./LightTrappingSiliconSolarCells.sqlite"; + } + + // 4.2 Here, we pick another sheet named 'Wavelength thickness trapping' from the Workbook + block SecondLightTrappingSiliconSolarCellsSheetpicker oftype SheetPicker { + sheetName: 'Wavelength thickness trapping'; + } + + block SecondLightTrappingSiliconSolarCellsTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "n" oftype decimal, + "Wavelength (µm)" oftype decimal, + ]; + } + + block SecondLightTrappingSiliconSolarCellsLoader oftype SQLiteLoader { + + table: "SecondLightTrappingSiliconSolarCells"; + file: "./LightTrappingSiliconSolarCells.sqlite"; + } + + LightTrappingSiliconSolarCellsExtractor + -> LightTrappingSiliconSolarCellsTextXLSXInterpreter + -> LightTrappingSiliconSolarCellsSheetpicker + -> NameHeaderWriter + -> LightTrappingSiliconSolarCellsTableInterpreter + -> LightTrappingSiliconSolarCellsLoader; + + // 5. Once the XLSX file is interpreted, we can split the pipeline and + // work separately on the different sheets from our input file + LightTrappingSiliconSolarCellsTextXLSXInterpreter + -> SecondLightTrappingSiliconSolarCellsSheetpicker + -> SecondLightTrappingSiliconSolarCellsTableInterpreter + -> SecondLightTrappingSiliconSolarCellsLoader; +} +``` \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.2.0/user/examples/workbooks-xlsx.md.license b/apps/docs/versioned_docs/version-0.2.0/user/examples/workbooks-xlsx.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/examples/workbooks-xlsx.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.2.0/user/expressions.md b/apps/docs/versioned_docs/version-0.2.0/user/expressions.md new file mode 100644 index 00000000..99e612a5 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/expressions.md @@ -0,0 +1,80 @@ +--- +sidebar_position: 7 +--- + +# Expressions + +Expressions in Jayvee are arbitrarily nested statements. They consist of: +- literals (e.g., numbers `5` or strings `"Example"`) +- variables (e.g., declared by `from` properties in [Transforms](./transforms.md)) +- operators (e.g., `*` or `sqrt`) + +Expressions get evaluated at runtime by the interpreter to a [Built-in ValueType](./valuetypes/builtin-valuetypes). + +### Example + +The following expression is evaluated to the `integer` `10`: `(2 + 3) * 2` + +The following expression is evaluated to the `integer` `3`: `floor (3.14)` + +The following expression is evaluated to the `boolean` `true`: `"Example" == "Example"` + +### List of Operators + +#### Arithmetics (binary operators) +- `+` for addition, e.g., `5 + 3` evaluates to `8` +- `-` for subtraction, e.g., `5 - 3` evaluates to `2` +- `*` for multiplication, e.g., `5 * 3` evaluates to `15` +- `/` for division, e.g., `6 / 3` evaluates to `2` +- `%` for modulo, e.g., `5 % 3` evaluates to `2` +- `pow` for power, e.g., `2 pow 3` evaluates to `8` +- `root` for root, e.g., `27 root 3` evaluates to `3` + +#### Arithmetics (unary operators) +- `+` for positive signing, e.g., `+5` evaluates to `5` +- `-` for negative signing, e.g., `-5` evaluates to `-5` +- `sqrt` for square root, e.g., `sqrt 9` evaluates to `3` +- `foor` for flooring a number, e.g., `floor 5.3` evaluates to `5` +- `ceil` for ceiling a number, e.g., `floor 5.3` evaluates to `6` +- `round` for rounding a number, e.g., `floor 5.3` evaluates to `5` + +#### Relational (binary operators) +- `<` for smaller, e.g., `3 < 3` evaluates to `false` +- `<=` for smaller or equal, e.g., `3 <= 3` evaluates to `true` +- `>` for greater, e.g., `3 > 3` evaluates to `false` +- `>=` for greater or equal, e.g., `3 >= 3` evaluates to `true` +- `==` for equal, e.g., `3 == 3` evaluates to `true` +- `!=` for not equal, e.g., `3 != 3` evaluates to `false` + +#### Logical (binary operators) +- `and` for a logical and (both need to be true to evaluate to true) +- `or` for a logical or (at least left or right needs to be true to evaluate to true) +- `xor` for a logical xor (either left or right needs to be true to evaluate to true) + +#### Logical (unary operators) +- `not` for logical negation, `not true` evaluates to `false` + +#### Others (binary operators) +- `matches` for a regex match, e.g., `"A07" matches /^[A-Z0-9]*$/` evaluates to `true` +- `in` for inclusion in an array, e.g., `"a" in ["a", "b", "c"]` evaluates to `true` + +### Operator Details + +#### `in` Operator + +The `in` operator checks whether a value is included in a collection of values. For example: + +```jayvee +4.5 in [3, 6.5] // evaluates to false +3 in [3.0, 6.5] // evaluates to true +"a" in ["a", "b", "c"] // evaluates to true +``` + +The operator supports `text`, `integer` and `decimal` values as operands. The compatibility of left and right operand types follows these rules: +- For the `in` operator we have a type for the needle (left operand) and a type for the elements in the haystack (right operand). +- There is an automated type conversion as long as it is lossless and clearly defined (integer to decimal as of now). +- We allow any combination of operands that has either: (i) An automated type conversion from needle type (left operand) to the type of the elements in the haystack (right operand), or (ii) the other way around. + + +### Further reading +For a deeper documentation of how expressions and operators work internally, refer to the [developer docs](../dev/04-guides/04-expressions-and-operators.md). \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.2.0/user/expressions.md.license b/apps/docs/versioned_docs/version-0.2.0/user/expressions.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/expressions.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.2.0/user/intro.md b/apps/docs/versioned_docs/version-0.2.0/user/intro.md new file mode 100644 index 00000000..2832707c --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/intro.md @@ -0,0 +1,95 @@ +--- +sidebar_position: 1 +--- + +# Introduction to Jayvee + +Jayvee is a domain-specific language (DSL) for data engineering - the cleaning and preprocessing of data for later activities like data science or machine learning. You can use Jayvee to **model an ETL pipeline** and the command-line interpreter to **run the ETL pipeline** on your local machine. + +## Installation + +Install the interpreter via `npm`. You will need a **nodejs version >= 17.0.0**. + +```bash +npm install -g @jvalue/jayvee-interpreter +``` + +You can install a specific version using the `@`-syntax, e.g., version `0.0.17`: +```bash +npm install -g @jvalue/jayvee-interpreter@0.0.17 +``` + +## Update + +Updating the interpreter is done by reinstalling it using `npm`. Make sure to also update the [VSCode plugin](#vscode-plugin) to match the installed interpreter if you use it. + +```bash +npm install -g @jvalue/jayvee-interpreter +``` + +## Usage + +### Show help + +```console +jv -h +``` + +### Run a `.jv` file + +```console +jv <file> +``` + +Run with **additional debug output**: + +```console +jv <file> -d +``` + + +With **runtime parameters**: + +```console +jv -e <param>=<value> -e <param>=<value> ... <file> +``` + +### Debug a `.jv` file + +Print debugging is further configured by the parameters `--debug-granularity` and `--debug-target`. + +```console +jv <file> -d -dg peek +``` +The value of the parameter `--debug-granularity` (short `-dg`) can have the following values: +- `peek` to log a short summary, including a small subset of data +- `exhaustive` to log a summary, including the full data +- `minimal` to log a summary, including no additional data (default). +To see logs, debugging has to be enabled using the `-d` flag. + +```console +jv <file> -d --debug-granularity peek +``` +The parameter `--debug-target` (short `-dt`) allows to specify which blocks should be logged for debugging. Separate block names by comma if multiple blocks are targeted. All blocks are logged if the parameter is omitted. +```console +jv <file> -d --debug-granularity peek --debug-target MyExtractorBlock,MySinkBlock +``` + + +## Examples + +You can find multiple examples [here](https://github.com/jvalue/jayvee/tree/main/example). Copy them to your local file system and execute them with the `jv` command on your command line (see [usage](#usage)). + + +## VSCode Plugin + +To set up Jayvee locally in VS Code, you need to install the latest Jayvee VS Code extension. +To install the most recent extension, go to our [latest release](https://github.com/jvalue/jayvee/releases/latest) +and download the `jayvee.vsix` file from the release assets. +Next, go to [this page](https://code.visualstudio.com/docs/editor/extension-marketplace#_install-from-a-vsix) and +follow the instructions for installing the downloaded extension. + +## Troubleshooting + +1. Error `structuredClone is not defined` + * Please make sure you use node version 17+. diff --git a/apps/docs/versioned_docs/version-0.2.0/user/intro.md.license b/apps/docs/versioned_docs/version-0.2.0/user/intro.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/intro.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.2.0/user/runtime-parameters.md b/apps/docs/versioned_docs/version-0.2.0/user/runtime-parameters.md new file mode 100644 index 00000000..bbdab1ed --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/runtime-parameters.md @@ -0,0 +1,27 @@ +--- +sidebar_position: 9 +--- + +# Runtime Parameters + +Property values in Jayvee can be assigned to `values` or left open for later configuration via `runtime parameters`. + +## Syntax + +Runtime parameters are indicated by the `requires` keyword, followed by the identifier of the parameter. Example + +```jayvee +block CarsLoader oftype SQLiteLoader { + table: "Cars"; + file: requires CARS_SQLITE_FILE; +} +``` + +## CLI + +The interpreter CLI has to define all existing runtime parameters for execution. +Use the CLI flag `-e` to to define them as key-value pairs of identifier and value. + +```console +jv -e <param>=<value> -e <param>=<value> ... <file> +``` diff --git a/apps/docs/versioned_docs/version-0.2.0/user/runtime-parameters.md.license b/apps/docs/versioned_docs/version-0.2.0/user/runtime-parameters.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/runtime-parameters.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.2.0/user/transforms.md b/apps/docs/versioned_docs/version-0.2.0/user/transforms.md new file mode 100644 index 00000000..bf3dfd7d --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/transforms.md @@ -0,0 +1,77 @@ +--- +sidebar_position: 8 +--- + +# Transforms + +Transforms are a concept in Jayvee to define the transformation of individual values. +They are similar to functions in programming languages, i.e. they perform computations on some input values and produce output values. Transform work by mapping input values to outputs using [expressions](./expressions.md). + +:::info Important + +Up to version `0.0.16`, we only supported a single input for transformers! + +::: + +:::info Important + +In its current state, Jayvee only supports a arbitrary numbers of inputs and a single output for transforms. +For the future, it is planned to support arbitrary numbers for outputs as well. + +::: + + +## Syntax + +The general syntax of transforms looks like this: + +```jayvee +transform <name> { + from <inputName> oftype <inputValuetype>; + to <outputName> oftype <outputValuetype>; + + <outputName>: <expression>; +} +``` + +The `transform` keyword is used to define a transform and give it a name. +The curly braces denote the body of the transform. + +The body first contains the definitions of input and output ports. +Input ports are defined using the `from` keyword whereas output ports use the `to` keyword. +Next, they are given a name and, after the `oftype` keyword, typed with a valuetype. + +Below, there needs to be an output assignment for each output port. +The output assignment defines how a particular output value is computed. +They consist of the name of an output port, followed by a `:`. +Next, an [expression](./expressions.md) specifies how the output value shall be computed. +Names of input ports can be used in such an expression to refer to input values. + +### Example + +The following transform converts temperature values from degree Celsius to Kelvin: + +```jayvee +transform CelsiusToKelvin { + from tempCelsius oftype decimal; + to tempKelvin oftype decimal; + + tempKelvin: tempCelsius + 273.15; +} +``` + +The following transform converts a text based status into a boolean value, `true` if the text is `Active`, `false` for any other value: + +```jayvee +transform StatusToBoolean { + from statusText oftype text; + to statusBoolean oftype boolean; + + statusBoolean: statusText == "Active"; +} +``` + +## Applying transforms to table columns + +Transforms can be applied to columns of a table. +Please refer to the documentation of the [`TableTransformer` block type](./block-types/TableTransformer.md) to find out how. diff --git a/apps/docs/versioned_docs/version-0.2.0/user/transforms.md.license b/apps/docs/versioned_docs/version-0.2.0/user/transforms.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/transforms.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.2.0/user/valuetypes/_category_.json b/apps/docs/versioned_docs/version-0.2.0/user/valuetypes/_category_.json new file mode 100644 index 00000000..ed1ce95f --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/valuetypes/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Valuetypes", + "position": 5, + "link": { + "type": "generated-index", + "description": "Jayvee supports these different kinds of valuetypes." + } +} diff --git a/apps/docs/versioned_docs/version-0.2.0/user/valuetypes/_category_.json.license b/apps/docs/versioned_docs/version-0.2.0/user/valuetypes/_category_.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/valuetypes/_category_.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.2.0/user/valuetypes/builtin-valuetypes.md b/apps/docs/versioned_docs/version-0.2.0/user/valuetypes/builtin-valuetypes.md new file mode 100644 index 00000000..0bc36dcb --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/valuetypes/builtin-valuetypes.md @@ -0,0 +1,98 @@ +--- +title: Built-in Valuetypes +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +# Description + +For an introduction to valuetypes, see the [Core Concepts](../core-concepts). +Built-in valuetypes come with the basic version of Jayvee. +They are the basis for more restricted [Primitive Valuetypes](./primitive-valuetypes) +that fullfil [Constraints](./primitive-valuetypes#constraints). + +# Available built-in valuetypes + +## Boolean + +### Description + +A boolean value. +Examples: true, false + +### Example 1 + +```jayvee +block ExampleTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "columnName" oftype boolean + ]; +} +``` + +A block of type `TableInterpreter` that + interprets data in the column `columnName` as `boolean`. + +## Decimal + +### Description + +A decimal value. +Example: 3.14 + +### Example 1 + +```jayvee +block ExampleTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "columnName" oftype decimal + ]; +} +``` + +A block of type `TableInterpreter` that + interprets data in the column `columnName` as `decimal`. + +## Integer + +### Description + +An integer value. +Example: 3 + +### Example 1 + +```jayvee +block ExampleTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "columnName" oftype integer + ]; +} +``` + +A block of type `TableInterpreter` that + interprets data in the column `columnName` as `integer`. + +## Text + +### Description + +A text value. +Example: "Hello World" + +### Example 1 + +```jayvee +block ExampleTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "columnName" oftype text + ]; +} +``` + +A block of type `TableInterpreter` that + interprets data in the column `columnName` as `text`. diff --git a/apps/docs/versioned_docs/version-0.2.0/user/valuetypes/builtin-valuetypes.md.license b/apps/docs/versioned_docs/version-0.2.0/user/valuetypes/builtin-valuetypes.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/valuetypes/builtin-valuetypes.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.2.0/user/valuetypes/primitive-valuetypes.md b/apps/docs/versioned_docs/version-0.2.0/user/valuetypes/primitive-valuetypes.md new file mode 100644 index 00000000..92400a53 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/valuetypes/primitive-valuetypes.md @@ -0,0 +1,48 @@ +--- +sidebar_position: 2 +--- +# Primitive ValueTypes + +`Primitive ValueTypes` are based on `Built-in ValueTypes` and use a collection of constraints to restrict the range of valid values. +Such constraints are implicitly connected via a logical `AND` relation. +Note that the `Constraints` need to be applicable to the base-type of the `ValueType` - indicated by the identifier after the keyword `oftype`: + +```jayvee +valuetype GasFillLevel oftype integer { + constraints: [ GasFillLevelRange ]; +} +``` + + +## Constraints + +`Constraints` for `ValueTypes` declare the validity criteria that each concrete value is checked against. + +### Syntax 1: Expression syntax + +The syntax of expression-based `Constraints` uses an expression that evaluates to `true` or `false` for the given `value`. The type of the values the expression is working in is indicated ofter the keyword `on`: + +```jayvee +constraint GasFillLevelRange on decimal: + value >= 0 and value <= 100; +``` + +Refer to the [Expression documentation](../expressions.md) for further reading on expressions. + + +### Syntax 2: Block-like syntax + +The syntax of `Constraints` is similar to the syntax of `Blocks`. +The availability of property keys and their respective `ValueTypes` is determined by the type of the `Constraint` - indicated by the identifier after the keyword `oftype`: + +```jayvee +constraint GasFillLevelRange oftype RangeConstraint { + lowerBound: 0; + lowerBoundInclusive: true; + upperBound: 100; + upperBoundInclusive: true; +} +``` + +Note that the type of `Constraint` also determines its applicability to `ValueTypes`. +For instance, a `RangeConstraint` can only be applied to the numerical types `integer` and `decimal`. \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.2.0/user/valuetypes/primitive-valuetypes.md.license b/apps/docs/versioned_docs/version-0.2.0/user/valuetypes/primitive-valuetypes.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.2.0/user/valuetypes/primitive-valuetypes.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.3.0/dev/01-intro.md b/apps/docs/versioned_docs/version-0.3.0/dev/01-intro.md new file mode 100644 index 00000000..fef3c08e --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/dev/01-intro.md @@ -0,0 +1,52 @@ +--- +sidebar_position: 1 +--- + +# Introduction for Jayvee Developers + +## How to contribute + +In order to contribute to the Jayvee project, please have a look at our [contribution guide](https://github.com/jvalue/jayvee/blob/main/CONTRIBUTING.md). Before planning a contribution, please read the [design principles](./05-design-principles.md) and consider if your changes fit the vision expressed there. + +The overall project is licensed under the `AGPL-3.0-only` license and is compliant to the [REUSE Specification](https://reuse.software/spec/) by the [Free Software Foundation Europe](https://fsfe.org/). +Because of this, contributions are required to adhere to the license and follow that specification. +More details on this topic can be found [here](./02-dev-processes/03-licensing-and-copyright.md). + +And last but not least, please read and follow our [code of conduct](https://github.com/jvalue/jayvee/blob/main/CODE_OF_CONDUCT.md). + +## Project overview + +The Jayvee repository is located [here](https://github.com/jvalue/jayvee) on GitHub. +It uses an [Nx mono-repository](https://nx.dev/) setup and contains all related projects, most notably the Jayvee language server and interpreter. +Please have a look at the [architecture overview](./03-architecture-overview.md) for more details. + +## Prerequisites + +Node.js and npm are required for development. +It is recommended to use their LTS version to avoid any potential compatibility issues. +Also, refer to this [quick start guide](https://github.com/jvalue/jayvee#development-quickstart) for developers. + +In order to run the Jayvee VS Code extension during development, VS Code is required as IDE. +Using VS Code also allows installing the [Langium VS Code extension](https://marketplace.visualstudio.com/items?itemName=langium.langium-vscode) for better IDE support when working with Langium grammar files. + +## Resources for getting started + +### Langium + +- [**Langium documentation**](https://langium.org/docs/) +- Practical examples using Langium: + - [Langium examples](https://github.com/langium/langium/tree/main/examples): Official example languages by Langium + - [Langium's grammar language](https://github.com/langium/langium/tree/main/packages/langium): The implementation of Langium's own grammar language + - [Language server for SQL](https://github.com/langium/langium-sql): An implementation of a language server for SQL + - [MiniLogo DSL](https://github.com/langium/langium-minilogo): A domain-specific language for drawing pictures + - [Lox language](https://github.com/langium/langium-lox): An implementation of the Lox scripting language (also see the "Crafting Interpreters" book below) + - [SimpleUI DSL](https://github.com/TypeFox/langium-ui-framework): A domain-specific language for generating user interfaces + - [STPA-DSL](https://github.com/kieler/stpa): A domain-specific language for System-Theoretic Process Analysis +- [Langium playground](https://langium.org/playground/): A tool for testing Langium grammars and visualizing resulting ASTs +- [Typefox blog](https://www.typefox.io/blog/): A blog by the company behind Langium, mostly posting Langium-related content. + +### Building compilers / interpreters / DSLs + +- [Crafting Interpreters](https://craftinginterpreters.com/contents.html): A book by Robert Nystrom on implementing an interpreter for the Lox scripting language +- [DSL Engineering](https://voelter.de/dslbook/markusvoelter-dslengineering-1.0.pdf): A book by Markus Voelter on designing, implementing and using domain-specific languages +- [Awesome Compilers](https://github.com/aalhour/awesome-compilers#readme): A list featuring many resources on compilers and interpreters diff --git a/apps/docs/versioned_docs/version-0.3.0/dev/01-intro.md.license b/apps/docs/versioned_docs/version-0.3.0/dev/01-intro.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/dev/01-intro.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.3.0/dev/02-dev-processes/01-rfc-process.md b/apps/docs/versioned_docs/version-0.3.0/dev/02-dev-processes/01-rfc-process.md new file mode 100644 index 00000000..07444085 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/dev/02-dev-processes/01-rfc-process.md @@ -0,0 +1,13 @@ +--- +title: Language Design Process (RFCs) +sidebar_position: 1 +--- + +We use RFCs to discuss changes to the language before implementing them. You can have a look at all closed (accepted / rejected) RFCs [here](https://github.com/jvalue/jayvee/pulls?q=is%3Apr+is%3Aclosed+RFC+), and all RFCs under discussion [here](https://github.com/jvalue/jayvee/pulls?q=is%3Apr+is%3Aopen+RFC). + +If you want to contribute an RFC please follow these steps: +1. Make a copy of the **template** at `rfc/0000-rfc-template.md` and place it into the `rfc` folder. +2. Create a draft for the RFC on a new branch. Follow the `TODOs` in template to do so. +3. Open a pull request with prefix `RFC <number>` in the title. +4. Address the reviews. Consider opening a new PR if major things need to be addressed and the discussion log becomes too confusing. +5. Once accepted, create an issue with the necessary steps to implement the RFC. \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.3.0/dev/02-dev-processes/01-rfc-process.md.license b/apps/docs/versioned_docs/version-0.3.0/dev/02-dev-processes/01-rfc-process.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/dev/02-dev-processes/01-rfc-process.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.3.0/dev/02-dev-processes/02-debug-vs-code-extension.md b/apps/docs/versioned_docs/version-0.3.0/dev/02-dev-processes/02-debug-vs-code-extension.md new file mode 100644 index 00000000..5fbcf07c --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/dev/02-dev-processes/02-debug-vs-code-extension.md @@ -0,0 +1,25 @@ +--- +title: Debugging via the VS Code extension +sidebar_position: 2 +--- + +The VS Code extension of Jayvee can be used to interactively debug the language server. +During development, when using VS Code as IDE, another instance of VS Code can be opened which has the most recent, locally built VS Code extension loaded. +This can be achieved using the launch configuration `Run extension` the `Run and Debug` side-menu of VS Code or by pressing the `F5` key if that launch configuration is already selected there. + +Note that there is no file watching mechanism involved. +So in order to reflect changes to the source code, the additional VS Code instance has to be **closed and reopened** as described above. + +## How to attach a debugger + +1. Use the launch configuration `Run extension` to open the additional VS Code instance with the extension loaded +2. Use the launch configuration `Attach to Language Server` to attach the debugger + +Any set breakpoints should now be marked as active and pause the execution once they are reached. + +## How to view logs of the language server + +In the additional VS Code instance, it is possible to view the logs of the language server. +They might also be helpful for debugging since any uncaught errors are logged with their stack trace by the Langium framework. + +To view the logs, open the bottom panel called `Output` and select `Jayvee` in the dropdown menu. diff --git a/apps/docs/versioned_docs/version-0.3.0/dev/02-dev-processes/02-debug-vs-code-extension.md.license b/apps/docs/versioned_docs/version-0.3.0/dev/02-dev-processes/02-debug-vs-code-extension.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/dev/02-dev-processes/02-debug-vs-code-extension.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.3.0/dev/02-dev-processes/03-licensing-and-copyright.md b/apps/docs/versioned_docs/version-0.3.0/dev/02-dev-processes/03-licensing-and-copyright.md new file mode 100644 index 00000000..201e21ca --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/dev/02-dev-processes/03-licensing-and-copyright.md @@ -0,0 +1,69 @@ +--- +title: Licensing and copyright +sidebar_position: 3 +--- + +The [Jayvee repository](https://github.com/jvalue/jayvee) is REUSE compliant, meaning that it fulfills the +[REUSE Specification](https://reuse.software/spec/) by the [Free Software Foundation Europe](https://fsfe.org/) (FSFE). +This makes clear under which license each project file is licensed under and who owns the copyright, not only for +humans but also for machines. + +This is done by explicitly adding copyright and licensing information to each file of the project. This is achieved +by either using a comment header or a separate `*.license` file in case comments are not possible. + +See <https://reuse.software/> more information. + +## What license is used in this project and who is the copyright holder? + +The entire project is licensed under [AGPL-3.0-only](https://spdx.org/licenses/AGPL-3.0-only.html), the +license file can be found [here](https://github.com/jvalue/jayvee/blob/main/LICENSES/AGPL-3.0-only.txt). +The copyright holder is the [Friedrich-Alexander-Universität Erlangen-Nürnberg](https://www.fau.eu/). + +## How to submit a REUSE compliant contribution? + +In case you want to contribute to the project, you will need to ensure that all of your contributed files are REUSE +compliant. In order to achieve this, you need to add a key-value pair for both copyright and licensing information +following this schema: + +``` +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only +``` + +In case a file allows comments, use single-line comments to add the copyright and licensing information at the top +of the file. Otherwise, create a corresponding `*.license` file with the text above as its content. You can have a +look at some existing project files to get an impression on it is done in practice. + +For files with common file extensions, you can use the [reuse CLI tool](https://github.com/fsfe/reuse-tool) to add +licensing and copyright information automatically. + +For more details, you can have a look at the [Getting Started tutorial](https://reuse.software/tutorial/) on the REUSE +website. + +## How to validate REUSE compliance? + +When you make a contribution and open a new pull request, the CI checks whether your contribution is REUSE compliant +using the [reuse CLI tool](https://github.com/fsfe/reuse-tool). + +In order to validate REUSE compliance in your local development environment, you have to install the +[reuse CLI tool](https://github.com/fsfe/reuse-tool) and run the following command in the projects' root folder: + +```bash +reuse lint +``` + +You can also set up a pre-commit hook, so the above command is run before each commit. +See [here](https://reuse.readthedocs.io/en/latest/readme.html#run-as-pre-commit-hook) for details on how to set it up. + +## How to hide `*.license` files in IDEs + +During development, the file explorer of your IDE may be cluttered due to the numerous `*.license` files in the +project. Luckily, most IDEs allow hiding certain files, e.g. by specifying a pattern to exclude them from the +explorer. + +Below, you can find instructions on how to hide `*.license` files in commonly used IDEs: +- **Visual Studio Code**: Add `**/*.license` to the +[`files.exclude` setting](https://code.visualstudio.com/docs/getstarted/userinterface#_explorer) +- **WebStorm**: Add `*.license` to the +[excluded files setting](https://www.jetbrains.com/help/webstorm/configuring-project-structure.html#exclude-by-pattern) diff --git a/apps/docs/versioned_docs/version-0.3.0/dev/02-dev-processes/03-licensing-and-copyright.md.license b/apps/docs/versioned_docs/version-0.3.0/dev/02-dev-processes/03-licensing-and-copyright.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/dev/02-dev-processes/03-licensing-and-copyright.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.3.0/dev/02-dev-processes/04-release-jayvee-version.md b/apps/docs/versioned_docs/version-0.3.0/dev/02-dev-processes/04-release-jayvee-version.md new file mode 100644 index 00000000..374f3468 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/dev/02-dev-processes/04-release-jayvee-version.md @@ -0,0 +1,22 @@ +--- +title: Releasing a new Jayvee version +sidebar_position: 4 +--- + +## Version Numbers + +In this early stage of the project we do not yet follow [semantic versioning](https://semver.org/) since we expect the introduction of breaking changes frequently. +To indicate that, we only release alpha versions where the `version` is incremented with every release. +- For the npm packages, we use the version `0.0.<version>`. +- For the GitHub releases, we use the git tag `v0.0.<version>-alpha`. + +## Jayvee Release Procedure + +For releasing a new version of Jayvee, you need to complete the following steps: + +1. Increment the version in the `package.json` file. +2. Run `npm i` to update the `package-lock.json`. +3. Run `npx nx run docs:version-snapshot` to generate a snapshot of the docs for the previous version. +4. If you are on a feature or dev branch, merge into main. +5. Create a GitHub release on the main branch. Attach a changelog. +6. The CI/CD will deal with the rest. diff --git a/apps/docs/versioned_docs/version-0.3.0/dev/02-dev-processes/04-release-jayvee-version.md.license b/apps/docs/versioned_docs/version-0.3.0/dev/02-dev-processes/04-release-jayvee-version.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/dev/02-dev-processes/04-release-jayvee-version.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.3.0/dev/02-dev-processes/_category_.json b/apps/docs/versioned_docs/version-0.3.0/dev/02-dev-processes/_category_.json new file mode 100644 index 00000000..c8f51245 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/dev/02-dev-processes/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Development Process", + "position": 2, + "link": { + "type": "generated-index", + "description": "Here you can find general processes around developing Jayvee." + } +} diff --git a/apps/docs/versioned_docs/version-0.3.0/dev/02-dev-processes/_category_.json.license b/apps/docs/versioned_docs/version-0.3.0/dev/02-dev-processes/_category_.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/dev/02-dev-processes/_category_.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.3.0/dev/03-architecture-overview.md b/apps/docs/versioned_docs/version-0.3.0/dev/03-architecture-overview.md new file mode 100644 index 00000000..cb4576fa --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/dev/03-architecture-overview.md @@ -0,0 +1,43 @@ +--- +title: Architecture overview +sidebar_position: 4 +--- + +Jayvee has a clear separation between the language itself and its execution. +This guide gives an overview of the overall architecture. + +## Language Server + +On the pure language side, the central project is the [language server](https://github.com/jvalue/jayvee/tree/main/libs/language-server) which is heavily built upon the [Langium framework](https://langium.org/). +It contains the syntax definition (i.e. the grammar) and is capable of performing static semantic analysis on models, so invalid models can be rejected and errors are reported to the user. +It uses the [Language Server Protocol](https://microsoft.github.io/language-server-protocol/) (LSP) for communicating with IDEs in order to provide common features such as diagnostics, auto completion and much more. + +## Interpreter + +The Jayvee [interpreter](https://github.com/jvalue/jayvee/tree/main/apps/interpreter) on the other hand is capable of running Jayvee models. +Therefore, it uses the language server as a library to perform lexing, parsing and semantic analysis on given models. +In case no errors were found during these phases, it executes the model by interpreting it. +This means that models are not compiled to any other language, like a compiler does, but rather directly executed on the fly without any code generation involved. +The interpreter comes with a command-line interface (CLI) for users, so they are able to execute Jayvee models easily. + +The following diagram visualizes the architecture described so far: + +```mermaid +graph LR +model[Jayvee Model] --> lexical(Lexical Analysis) +subgraph Jayvee Interpreter + subgraph Jayvee Language Server + subgraph Langium Framework + lexical --> syntax(Syntax Analysis) + end + syntax --> semantic(Semantic Analysis) + end + semantic --> interpretation(Interpretation) +end +``` + +## Jayvee Extensions + +Lastly, there are Jayvee extensions for adding additional features to Jayvee. +See [here](./04-guides/06-jayvee-extensions.md) for more details. +It is worth pointing out that extensions also follow the separation between language and execution described above. diff --git a/apps/docs/versioned_docs/version-0.3.0/dev/03-architecture-overview.md.license b/apps/docs/versioned_docs/version-0.3.0/dev/03-architecture-overview.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/dev/03-architecture-overview.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.3.0/dev/04-guides/01-jayvee-grammar.md b/apps/docs/versioned_docs/version-0.3.0/dev/04-guides/01-jayvee-grammar.md new file mode 100644 index 00000000..356fb09d --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/dev/04-guides/01-jayvee-grammar.md @@ -0,0 +1,34 @@ +--- +title: The Jayvee grammar +sidebar_position: 1 +--- + +The grammar of Jayvee describes the syntax and structure of the language. +It is located in the language server project. +The grammar itself is written in [Langium's own grammar language](https://langium.org/docs/grammar-language/) which is similar to [Xtext](https://www.eclipse.org/Xtext/) grammars. +Such grammar files are easily identifiable via their `.langium` file extension. + +The grammar is used to generate TypeScript interfaces that represent the Abstract Syntax Tree (AST) and different, semantically equivalent files to define syntax highlighting for different applications. +For instance, a [TextMate](https://macromates.com/manual/en/language_grammars) file is generated for the syntax highlighting in the Jayvee VS Code extension whereas a [Monarch](https://microsoft.github.io/monaco-editor/monarch.html) file is generated for the syntax highlighting in the Monaco editor. + +For further information on the grammar language of Langium, visit the corresponding [Langium documentation](https://langium.org/docs/grammar-language/). + +## Working with the grammar + +To run the code generation, either use `npm run generate` for solely the code generation or `npm run build` for an entire build. The code generation also generates further code, like the standard library. + +Whenever the grammar is changed and the code generation is run during development, it is advisory to **close and reopen the IDE**, so the changes are noticed and the file indexing is updated. + +## How to rename AST nodes + +Renaming AST nodes is not as straight forward as one might initially assume. +When renaming individual rules in the grammar, just the generated TypeScript code in `ast.ts` will reflect the renaming, but not the rest of the codebase. + +The following steps explain how to rename an AST node, so the change is reflected in the entire codebase. As an example, an AST node called `A` is supposed to be renamed to `B`: + +- Open the `ast.ts` file in the language server project +- Locate the `A` interface / type and the `isA` typeguard +- Use the rename feature by the IDE to perform the renaming from `A` to `B` and from `isA` to `isB` +- Open the grammar and locate the `A` rule +- Use the rename feature by the Langium VS Code extension to perform the renaming from `A` to `B` +- Run `npm run generate` diff --git a/apps/docs/versioned_docs/version-0.3.0/dev/04-guides/01-jayvee-grammar.md.license b/apps/docs/versioned_docs/version-0.3.0/dev/04-guides/01-jayvee-grammar.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/dev/04-guides/01-jayvee-grammar.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.3.0/dev/04-guides/02-working-with-the-ast.md b/apps/docs/versioned_docs/version-0.3.0/dev/04-guides/02-working-with-the-ast.md new file mode 100644 index 00000000..895cae12 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/dev/04-guides/02-working-with-the-ast.md @@ -0,0 +1,148 @@ +--- +title: Working with the AST +sidebar_position: 2 +--- + +The nodes of the Abstract Syntax Tree (AST) consist of types and interfaces generated from the language grammar. +See [here](./01-jayvee-grammar.md) for more information on that topic. + +The following sections provide practical guides and tips for working with nodes of the AST. + +## Dealing with potentially incomplete AST nodes + +According to the generated interfaces, properties of AST nodes are never `undefined`. +In practice however, this is not always the case. + +For example, consider the language server being confronted with an incomplete Jayvee model with syntax errors. +In such cases, properties of AST nodes are in fact `undefined`, despite their interface definition. +In the interpreter however, after lexical and syntactic analysis succeeded, it can be assumed that all AST nodes are complete (i.e. they have no undefined properties) and even that all references are resolved. + +In order to avoid accessing properties of AST nodes that are potentially undefined, it is recommended to access them via the [`?.` operator](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#optional-chaining) rather than the regular `.` operator. +Note that this might result in a conflict with the ESLint rule [`@typescript-eslint/no-unnecessary-condition`](https://typescript-eslint.io/rules/no-unnecessary-condition/), so it has to be disabled manually for such cases. + +For disabling the rule in an entire file, place this comment below the copyright and licensing header: + +```typescript +/* eslint-disable @typescript-eslint/no-unnecessary-condition */ +``` + +For just a single line, place this comment above that particular line: + +```typescript +// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition +``` + +<details> + +<summary>Full example</summary> + +Consider an exemplary AST node `A` with a property `x` of type `string`. To access that property safely: + +```typescript +import { A } from './ast' + +const astNode: A; + +// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition +const property: string | undefined = astNode?.x; +``` + +</details> + +## Usage of `assertUnreachable` + +Most times, it is beneficial to make case distinctions exhaustive, especially when working with AST nodes, properties with a [union literal type](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types) or enums. +Exhaustiveness in this context means, that the TypeScript compiler is supposed to yield an error if a case distinction does not cover all possibilities. + +Langium offers a function called `assertUnreachable` which is capable of enforcing exhaustiveness and producing compiler errors in case of violations. See the following examples to get an idea on how to use it in practice: + +<details> + +<summary>Example for an exhaustive switch statement on a union literal type</summary> + +```typescript +import { assertUnreachable } from 'langium'; + +const operator: '+' | '-'; + +switch(operator) { + case '+': { + // ... + break; + } + case '-': { + // ... + break; + } + default: { + // To ensure the switch being exhaustive on `operator`: + assertUnreachable(operator); + } +} +``` + +</details> + +<details> + +<summary>Example for an exhaustive if-elseif-else cascade using typeguards</summary> + +Consider the exemplary AST nodes `A`, `B` and `C` and that `A = B | C`: + +```typescript +import { assertUnreachable } from 'langium'; +import { A, B, isB, C, isC } from './ast' + +const astNode: A; + +if (isB(astNode)) { + // `astNode` has type `B` here +} else if (isC(astNode)) { + // `astNode` has type `C` here +} else { + // To ensure the if-elseif-else cascade being exhaustive on `astNode`: + assertUnreachable(astNode); +} +``` + +</details> + +## Usage of `assert` for expressing runtime expectations + +During development, it may occur that a certain condition is expected to **be always true** at runtime. +For example, an AST node being of a certain type or a property being defined. +The type system of TypeScript is not always able to infer such facts, so developers may have to express these expectations explicitly in their code. +Examples are [type assertions](https://www.typescriptlang.org/docs/handbook/advanced-types.html) or the [non-null assertion operator](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html#non-null-assertion-operator). +Their usage may be problematic in case the condition does not always hold, e.g. due to a bug in the program or a wrong expectation by the programmer. +In such cases, it is hard to locate the origin and debug the program because such operations are erased in the compiled JavaScript code. + +To avoid these issues, it is recommended to express such expectations as boolean expressions that are actually validated at runtime. +This can be easily achieved by using the `assert` function. +It evaluates a given boolean expression and throws an `AssertionError` in case it evaluates to `false`. +After calling `assert`, the type system of TypeScript assumes the condition to be `true` and afterwards narrows types accordingly. + +Here is an example of how to use it in practice: + +```typescript +// Import the `assert` function like this: +import { strict as assert } from 'assert'; + +import { A, B, isB } from './ast'; + +const astNode: A; +assert(isB(astNode)); +// Here `astNode` has type `B` + +const referenced = astNode?.reference?.ref; +assert(referenced !== undefined); +// Here `referred` is not `undefined` +``` + +## AST wrapper classes + +The generated interfaces for AST nodes in `ast.ts` are only meant to represent the AST structurally, they don't define any behavior. +Also, in case of syntactic sugar, there may be different kinds of AST nodes representing the same semantic language concept. + +To cope with this problem, there is the concept of an `AstNodeWrapper`. +An AST node wrapper is capable of wrapping AST nodes that represent the same semantic language concept and adding behavior to them via custom methods. +To get an impression on how this can be done in practice, please have a look at the [`PipeWrapper` class](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/lib/ast/wrappers/pipe-wrapper.ts) and the [`AstNodeWrapper` interface](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/lib/ast/wrappers/ast-node-wrapper.ts). diff --git a/apps/docs/versioned_docs/version-0.3.0/dev/04-guides/02-working-with-the-ast.md.license b/apps/docs/versioned_docs/version-0.3.0/dev/04-guides/02-working-with-the-ast.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/dev/04-guides/02-working-with-the-ast.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.3.0/dev/04-guides/03-validation-and-diagnostics.md b/apps/docs/versioned_docs/version-0.3.0/dev/04-guides/03-validation-and-diagnostics.md new file mode 100644 index 00000000..328d5728 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/dev/04-guides/03-validation-and-diagnostics.md @@ -0,0 +1,64 @@ +--- +title: Validation and diagnostics +sidebar_position: 3 +--- + +Validation in the context of Jayvee can be understood as semantic analysis of Jayvee models. +The purpose is to uncover errors in models besides lexing, parsing and linking errors that the Langium framework already detects out of the box. +In case such errors are found, the language server makes the IDE display a diagnostic. +This means that the location of the error is underlined and an error message is shown. + +For example, each pipeline is expected to contain at least one starting block that requires no input. +Therefore, the language server analyzes every pipeline in a Jayvee model and checks for the presence of such a starting block. +In case no such block is found, the IDE underlines the pipeline definition in a red color and displays an error message. + +## How to implement validation + +### For arbitrary AST nodes + +In the file [`validation-registry.ts`](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/lib/validation/validation-registry.ts) of the language server, there is a registry containing validation functions for various AST nodes. +A validation function provided for a certain kind of AST node is called for every occurrence of such a node in a Jayvee model. +E.g. when registering a validation function for `PipelineDefinition` nodes, that function gets called once for each pipeline. + +A validation function always operates on a single, concrete AST node. +Besides the AST node, a `ValidationContext` object is passed to the function for reporting diagnostics via its `accept` method. + +The overall validation for a specific kind of AST node is usually subdivided into smaller checks that are called sequentially. +This potentially allows aborting the validation early, i.e. before all checks were run. +Aborting early may be useful if errors were reported during previous checks. +This can be determined via the `ValidationContext` which keeps track whether any errors occurred so far. + +A practical example where this plays a role are blocks: +When the type of a block is unknown to the language server, it makes no sense to validate inputs / outputs and properties of that block. +So, in case an error was reported during the check of the block type, the validation of that block is discontinued at that point. + +### For properties of blocks and constraints + +Validating property values of blocks / constraints works a bit differently because their individual validation is already incorporated into the overall validation framework. + +In general, the validation logic for a property values is located in the meta information of the block type / constraint type. +There, properties are defined using the `PropertySpecification` interface. +In order to add validation logic to a certain property, a validation function needs to be added to that property. + +For more complex validations that need to take multiple property values into account, it is also possible to provide a more general validation function which operates on the entire `PropertyBody` AST node. + +See [`text-range-selector-meta-inf.ts`](https://github.com/jvalue/jayvee/blob/main/libs/extensions/std/lang/src/text-range-selector-meta-inf.ts) for an example which includes both cases described above. + +## Reporting diagnostics + +During validation, diagnostics can be reported by calling the `accept` method of the given `ValidationContext` object. + +The call requires the severity of the diagnostic (`error`, `warning`, `info` or `hint`), the error message and its location. +The location is described by a `DiagnosticInfo` object which contains the affected AST node and optionally some further restrictions. +For more details, have a look at the [`DiagnosticInfo` interface](https://github.com/langium/langium/blob/main/packages/langium/src/validation/validation-registry.ts) by Langium. + +## Common issues + +When working on validations, a diagnostic with the following message may unexpectedly occur: `An error occurred during validation: ...` + +Such a diagnostic is generated by the Langium framework if an error object is thrown within the validation. +In most cases, the reason is the access of an `undefined` AST node property or an `AssertionError`. +For more details on such errors and how to avoid them, have a look at the documentation on [how to work with the AST](./02-working-with-the-ast.md). + +One way locate the origin of such errors is to debug the Jayvee VS Code extension, see [here](../02-dev-processes/02-debug-vs-code-extension.md) for more details. +Another possibility is to debug the interpreter and set appropriate breakpoints in the language server codebase. diff --git a/apps/docs/versioned_docs/version-0.3.0/dev/04-guides/03-validation-and-diagnostics.md.license b/apps/docs/versioned_docs/version-0.3.0/dev/04-guides/03-validation-and-diagnostics.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/dev/04-guides/03-validation-and-diagnostics.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.3.0/dev/04-guides/04-expressions-and-operators.md b/apps/docs/versioned_docs/version-0.3.0/dev/04-guides/04-expressions-and-operators.md new file mode 100644 index 00000000..91b51bd1 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/dev/04-guides/04-expressions-and-operators.md @@ -0,0 +1,151 @@ +--- +title: Expressions and operators +sidebar_position: 4 +--- + +Jayvee supports arbitrarily nested expressions and offers a variety of different operators. +Such expressions are similar to those used in programming languages, and they are intended to be read and understood intuitively. + +The following sections explain the definition of expressions in the language grammar, their type inference mechanism, and how the evaluation of expressions works. +As a small practical example, you may also have a look at the [arithmetics example by Langium](https://github.com/langium/langium/tree/main/examples/arithmetics) which has a heavy focus on mathematical expressions and functions. + +## Expressions in the grammar + +Expressions in the Jayvee grammar are defined in [`expression.langium`](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/grammar/expression.langium) and consist of operators (unary / binary) and literals. +Unary operators only have a single operand (e.g. the `not` operator) whereas binary operators require two operands (e.g. the `*` operator). + +The grammar is written in a way that literals end up in the leaves of the resulting AST and the nodes above represent the operators. + +As an example, have a look at the following AST structure of the expression `(-3 + 5) * 7`: + +```mermaid +classDiagram + +class `:BinaryOperator` { + operator = '*' +} + +%% Uses spaces in the name so it is unique +class ` :BinaryOperator` { + operator = '+' +} + +class `:UnaryOperator` { + operator = '-' +} + +%% Uses spaces in the name so it is unique +class ` :NumericLiteral` { + value = 3 +} + +%% Uses spaces in the name so it is unique +class ` :NumericLiteral` { + value = 5 +} + +class `:NumericLiteral` { + value = 7 +} + +`:BinaryOperator` --> ` :BinaryOperator` : leftOperand +`:BinaryOperator` --> `:NumericLiteral` : rightOperand +` :BinaryOperator` --> `:UnaryOperator` : leftOperand +`:UnaryOperator` --> ` :NumericLiteral` : operand +` :BinaryOperator` --> ` :NumericLiteral` : rightOperand +``` + +The AST is constructed in a way that ensures an unambiguous evaluation order when using depth-first search. +This implies that the AST structure already reflects the precedence and associativity of the operators, and that parentheses do not need to be explicitly represented. + +For more details on how such a grammar for expressions can be realized, see the [official Langium documentation](https://langium.org/docs/grammar-language/#tree-rewriting-actions), [this blog post](https://www.typefox.io/blog/parsing-expressions-with-xtext) and the following two sections which discuss the precedence and associativity of operators. + +### Precedence of operators + +The grammar implicitly defines the precedence of operators. +The operator precedence defines the order in which operators are evaluated within an expression. +For example, multiplication has a higher precedence than addition, which leads to multiplications being performed before additions. +In order to manually override such precedence conventions, parentheses can be used in expressions. + +The diagram below shows a more extensive precedence hierarchy, focused on common arithmetic operators. +Note that the hierarchy is arranged in ascending order, according to the operator precedence. +Such an order is similar to how operators in the actual Jayvee grammar are arranged: + +```mermaid +graph TD + subgraph BinaryOperators + add/sub(Addition Operator / Subtraction operator) --> mul/div(Multiplication Operator / Division Operator) + mul/div --> exp/root(Exponential Operator / Root Operator) + end + subgraph UnaryOperators + exp/root --> plus/minus(Plus Operator / Minus Operator) + end + plus/minus --> literal(Numeric Literal) +``` + +In the Jayvee grammar, every level of precedence is represented by a single grammar rule. +Within each such rule, the grammar rule which defines the operators with the next higher precedence is referenced. +E.g. the `AdditiveExpression` rule refers to the `MultiplicativeExpression` rule because multiplicative operators are supposed to have the next higher precedence. + +In order to alter the precedence of operators, their hierarchy of grammar rules has to be adjusted accordingly in [`expression.langium`](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/grammar/expression.langium). + +### Associativity of operators + +The associativity of operators defines the order in which operators with the same precedence are evaluated when they appear in succession without parentheses. +Operators may be either **left-associative**, **right-associative** or **non-associative**. +For example, depending on the associativity of the binary `+` operator, the expression `a + b + c` has different semantics: + +- Left-associative: `(a + b) + c` (evaluation from left to right) +- Right-associative: `a + (b + c)` (evaluation from right to left) +- Non-associative: _syntax error_, the operator cannot be chained + +The associativity of operators is also defined in the Jayvee grammar, more specifically by the structure of the grammar rules that define operators. +For details on how to encode the different kinds of associativity in grammar rules, have a look at the "Associativity" section near the end of [this blog post](https://www.typefox.io/blog/parsing-expressions-with-xtext). +The patterns described in the linked article can be found in [`expression.langium`](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/grammar/expression.langium), so each operator grammar rule encodes its own associativity. + +## Typing of expressions + +Jayvee has a mechanism for inferring and validating the type of a given expression. +This is achieved using a recursive algorithm which performs depth-first search on a given expression: + +The base case for the algorithm are literals, i.e. the tree leaves of an expression. +Depending on the type of literal, their type can be inferred trivially (in case of value literals) or otherwise from the context where the expression is located. + +For operators, the types of their operands are first inferred via recursion, and then it is checked whether they are supported by the operator. +Next, given the operand types, the resulting type is computed. +Such behavior is defined in a _type computer class_ located [here](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/lib/ast/expressions/type-computers). +Additionally, in [`operator-registry.ts`](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/lib/ast/expressions/operator-registry.ts), a type computer is registered for each kind of operator. + +In case the algorithm fails to infer a type, e.g. due to unsupported operand types or unresolved references in literals, the resulting type is `undefined`. +In order to report diagnostics in such cases, a `ValidationContext` object can be supplied when calling the type inference. + +## Evaluation of expressions + +The evaluation has the purpose of computing a single value out of an expression. +For a successful evaluation, it is a **precondition** that **a type could successfully be inferred** from the respective expression. +Also, the **value for each free variable** in that expression (i.e. literals resembling placeholders for values) needs to be **known beforehand**. +Therefore, an `ExecutionContext` object is passed to the evaluation which holds values for free variables in the current context. + +### Algorithm + +The algorithm for evaluating expressions is also based on a recursive depth-first search, similar to how the type inference works: + +Again, literals are the base case. A value literal trivially evaluates to its own value. +Other literals that resemble free variables evaluate to their value provided by the given `ExecutionContext` object. + +Regarding operators, their operands first are evaluated recursively. +Next, using the concrete operand values, the operator computes its resulting value. +This is defined in operator evaluator classes located [here](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/lib/ast/expressions/evaluators). +The classes are registered in [operator-registry.ts](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/lib/ast/expressions/operator-registry.ts) for every operator, similar to the previously mentioned type computers. + +The result of an evaluation may be `undefined` in case any errors occurred. +If the preconditions were all met, such errors are most likely arithmetic errors (like division by zero). +It is possible to provide a `ValidationContext` object to the evaluation for reporting such errors as diagnostics. + +### Evaluation strategies + +There are different evaluation strategies to choose from. +They affect the way, expressions are evaluated and thus have an impact on their semantics: + +- `exhaustive`: A full depth-first search is performed, all parts of an expression are evaluated. +- `lazy`: An evaluation with the least effort is performed. Logical operators use [short circuit semantics](https://en.wikipedia.org/wiki/Short-circuit_evaluation) and binary operators don't evaluate their right operand if the left one evaluated to `undefined`. diff --git a/apps/docs/versioned_docs/version-0.3.0/dev/04-guides/04-expressions-and-operators.md.license b/apps/docs/versioned_docs/version-0.3.0/dev/04-guides/04-expressions-and-operators.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/dev/04-guides/04-expressions-and-operators.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.3.0/dev/04-guides/05-standard-library.md b/apps/docs/versioned_docs/version-0.3.0/dev/04-guides/05-standard-library.md new file mode 100644 index 00000000..6db17bd7 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/dev/04-guides/05-standard-library.md @@ -0,0 +1,48 @@ +--- +title: Working with the Standard Library +sidebar_position: 5 +--- + +Jayvee ships with its own standard library on board, including the most often used valuetypes, transformations, and so on. +The standard library itself is written in `.jv` files [here](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/stdlib/). + +## Standard Library Contents + +The following elements are part of the standard library: + +## Builtin Contents + +The implementations of builtin contents are not expressed in Jayvee itself but on the TypeScript layer. Examples: + +- **Builtin valuetypes**: These valuetypes are the base for defining user-defined valuetypes in Jayvee, e.g., `text`, `integer`, `decimal`, `boolean`. +- **Builtin iotypes**: These iotypes are used to describe in inputs and outputs of blocktypes, e.g., `Sheet`, `File`. +- **Builtin blocktypes**: These blocktypes are the very basic building blocks in Jayvee, e.g., `HttpExtractor`, `SqliteLoader`. +- **Builtin constraint types**: These constraint types are constraints with custom logic, e.g., `LengthConstraint`, `RegexConstraint`. + +Builtin definitions are usually generated and added to the standard library from the internal representations of the concepts. + +### User-defined Contents + +The implementations of user-defined contents are expressed in Jayvee itself. Examples: + +- **User-defined valuetypes**: These valuetypes are based on builtin or other user-defined valuetypes. Their definition is expressed natively in Jayvee, e.g., `Percent`. +- **User-defined blocktypes**: These blocktypes are based on builtin or other user-defined blocktypes. Their definition is expressed natively in Jayvee. + +We use `jv` files to add user-defined valuetypes to the standard library (see below). + +## Extending the Standard Library + +Just add `jv` files to the directory [here](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/stdlib/). It is crawled hierarchically, meaning that you can also organize files in folders. + +## Implementation + +### 1. Code generation + +We use code generation to transform these `.jv` files into TypeScript files that the language server can used. The [generation script](https://github.com/jvalue/jayvee/tree/main/tools/scripts/language-server/generate-stdlib.mjs) is run via `npm run generate` next to the AST generation. + +### 2. Builtin libraries + +The solution we chose to implement the standard library mechanism is close to the [builtin library tutorial](https://langium.org/guides/builtin-library/) by Langium. The following components are of interest: + +- [JayveeWorkspaceManager](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/lib/builtin-library/jayvee-workspace-manager.ts) in the `language-server` that registers all libraries with the langium framework. +- [StandardLibraryFileSystemProvider](https://github.com/jvalue/jayvee/tree/main/apps/vs-code-extension/src/standard-library-file-system-provider.ts) in the `vs-code-extension` that registers all libraries with the vscode plugin framework. diff --git a/apps/docs/versioned_docs/version-0.3.0/dev/04-guides/05-standard-library.md.license b/apps/docs/versioned_docs/version-0.3.0/dev/04-guides/05-standard-library.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/dev/04-guides/05-standard-library.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.3.0/dev/04-guides/06-jayvee-extensions.md b/apps/docs/versioned_docs/version-0.3.0/dev/04-guides/06-jayvee-extensions.md new file mode 100644 index 00000000..107569d2 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/dev/04-guides/06-jayvee-extensions.md @@ -0,0 +1,190 @@ +--- +title: Jayvee Extensions +sidebar_position: 6 +--- + +## Concepts + +### Jayvee extension + +A Jayvee extension is used to add new block types to the language without having to modify the actual grammar of the language. Such a Jayvee extension usually consists of two parts: a language extension and an execution extension which are described in the upcoming two sections. + +Jayvee extensions that shall be used by default are bundled into the so-called [standard extension](https://github.com/jvalue/jayvee/tree/main/libs/extensions/std). That way, the [language server](https://github.com/jvalue/jayvee/tree/main/libs/language-server) and the [interpreter](https://github.com/jvalue/jayvee/tree/main/apps/interpreter) are able to load them out of the box. + +### Language extension + +A language extension defines meta information of block types which are required by the +[language server](https://github.com/jvalue/jayvee/tree/main/libs/language-server). +Such meta information describes properties of +block types such as their names, input / output types and their properties. + +Note that language extensions don't define any behavior. Instead, this is done by the corresponding execution extension. + +### Execution extension + +An execution extension defines behavior for block types. They build on the meta information from the corresponding +language extension, e.g. input / output types of the block need to match the signature of the execution method and +properties are accessed by their specified name. + +Execution extensions are only required by the [interpreter](https://github.com/jvalue/jayvee/tree/main/apps/interpreter) and not necessarily by the [language server](https://github.com/jvalue/jayvee/tree/main/libs/language-server) as they solely define behavior. + +## Recipes + +### Add a new Jayvee execution extension + +#### 1. Generate an execution libraries + +```bash +npx nx g @nrwl/node:library --name="extensions/<extension-name>/exec" +``` + +#### 2. Create extension classes + +In `libs/extensions/<extension-name>/exec/src/extension.ts`: + +```typescript +import { + BlockExecutorClass, + JayveeExecExtension, +} from '@jvalue/jayvee-execution'; + +export class MyExecExtension implements JayveeExecExtension { + getBlockExecutors(): BlockExecutorClass[] { + return []; + } +} +``` + +#### 3. Export extension classes + +In `libs/extensions/<extension-name>/exec/src/index.ts`: + +```typescript +export * from './extension'; +``` + +#### 4. Register new extension classes in the standard extension + +In `libs/extensions/std/exec/src/extension.ts`: + +```typescript +// ... + +import { MyExecExtension } from '@jvalue/jayvee-extensions/<extension-name>/exec'; + +export class StdExecExtension implements JayveeExecExtension { + private readonly wrappedExtensions: JayveeExecExtension[] = [ + // ... + // Register your execution extension here: + new MyExecExtension(), + // ... + ]; + + // ... +} +``` + +### Add a new block type in a Jayvee extension + +#### 1. Create a builtin blocktype + +Define the syntax of the new blocktype in the [language server's builtin blocktypes](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/stdlib/builtin-blocktypes). + +The following example defines a block type `MyExtractor` with a text property called `url` and a property `retries` with a default value: + +```jayvee +builtin blocktype MyExtractor { + input default oftype None; + output default oftype Sheet; + + property url oftype text; + property retries oftype interger: 10; +} +``` + +The new block type will be automatically registered on the language server startup. + +#### 2. Add custom validation logic (if required) + +If the block type and/or its properties requires custom validation logic, you can implement it in the [language server's block type specific checks](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/lib/validation/checks/blocktype-specific). + +#### 3. Implement `BlockExecutor` + +The following example implements an executor for the previously defined block type `MyExtractor`. + +The `execute` method defines the behavior when a block is executed. Its signature matches the input and output types defined in `MyExtractor.jv` file. + +In `libs/extensions/<extension-name>/exec/src/lib/my-extractor-executor.ts`: + +```typescript +import * as R from '@jvalue/jayvee-execution'; +import { + AbstractBlockExecutor, + BlockExecutorClass, + ExecutionContext, + Sheet, + implementsStatic, +} from '@jvalue/jayvee-execution'; +import { IOType, PrimitiveValuetypes } from '@jvalue/jayvee-language-server'; + +@implementsStatic<BlockExecutorClass>() +export class MyExtractorExecutor + extends AbstractBlockExecutor<IOType.NONE, IOType.SHEET> +{ + // Needs to match the type in meta information: + public static readonly type = 'MyExtractor'; + + public readonly inputType = IOType.NONE; + public readonly outputType = IOType.SHEET; + + async doExecute( + input: None, + context: ExecutionContext, + ): Promise<R.Result<Sheet>> { + // Accessing property values by their name: + const url = context.getPropertyValue( + 'url', + PrimitiveValuetypes.Text, + ); + const limit = context.getPropertyValue( + 'limit', + PrimitiveValuetypes.Integer, + ); + + // ... + + if (error) { + return R.err(...); + } + + return R.ok(...); + } +} +``` + +> **Info** +> The interface `BlockExecutor<I,O>` is used as an API for block executors. The abstract class `AbstractBlockExecutor<I,O>` gives some further functionality for free, e.g., debug logging. + +> **Warning** +> The generic types of `AbstractBlockExecutor<I,O>` need to match the input and output types of the corresponding `blocktype` definition. + +#### 4. Register the new `BlockExecutor` in the execution extension + +In `libs/extensions/<extension-name>/exec/src/extension.ts`: + +```typescript +// ... + +import { MyExtractorExecutor } from './lib/my-extractor-executor'; + +export class MyExecExtension implements JayveeExecExtension { + getBlockExecutors(): BlockExecutorClass[] { + return [ + // ... + // Register your block executor here: + MyExtractorExecutor, + // ... + ]; + } +} +``` diff --git a/apps/docs/versioned_docs/version-0.3.0/dev/04-guides/06-jayvee-extensions.md.license b/apps/docs/versioned_docs/version-0.3.0/dev/04-guides/06-jayvee-extensions.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/dev/04-guides/06-jayvee-extensions.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.3.0/dev/04-guides/_category_.json b/apps/docs/versioned_docs/version-0.3.0/dev/04-guides/_category_.json new file mode 100644 index 00000000..80f6fce8 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/dev/04-guides/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Guides", + "position": 4, + "link": { + "type": "generated-index", + "description": "Here you can find guides that will help you developing certain aspects of Jayvee." + } +} diff --git a/apps/docs/versioned_docs/version-0.3.0/dev/04-guides/_category_.json.license b/apps/docs/versioned_docs/version-0.3.0/dev/04-guides/_category_.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/dev/04-guides/_category_.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.3.0/dev/05-design-principles.md b/apps/docs/versioned_docs/version-0.3.0/dev/05-design-principles.md new file mode 100644 index 00000000..8b15dd8c --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/dev/05-design-principles.md @@ -0,0 +1,24 @@ +--- +title: Design Principles +sidebar_position: 5 +--- + +When deciding on new features for the domain-specific language itself, we try to adhere to the following high level guidelines. Of course, none of these statements is set in stone and every decision is a tradeoff. + +## Jayvee Manifesto +_Inspired by the [Agile Manifesto](https://agilemanifesto.org/)._ + +We are uncovering better ways of _modeling data pipelines by providing a domain-specific language for data engineering and making it easy for everyone to participate in it_. + +Through this work we have come to value: + +1. **Describing a goal state** over how to get there. +2. **Explicit modeling** over hidden magic. +3. **Composition** over inheritance. +4. **Flat structures** over deep nesting. + +That is, while there is value in the items on the right, we value the items on the left more. + +Through this work, we also have come to explore: + +5. **Libraries** over language features. \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.3.0/dev/05-design-principles.md.license b/apps/docs/versioned_docs/version-0.3.0/dev/05-design-principles.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/dev/05-design-principles.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.3.0/dev/12-jayvee-testing.md b/apps/docs/versioned_docs/version-0.3.0/dev/12-jayvee-testing.md new file mode 100644 index 00000000..1121c1ef --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/dev/12-jayvee-testing.md @@ -0,0 +1,254 @@ +--- +title: Writing tests for Jayvee +--- + +In order to ensure that Jayvee works as intended and to catch breaking changes, we have implemented the following components for regression testing: +- Testing utils: utils to create Langium Typescript objects from *.jv test assets (see [here](#testing-utils)) as well as mocks for execution testing (see [here](#testing-utils-1)) +- [Grammar tests](#grammar-tests): test the grammar parsing and validation +- [Execution tests](#execution-tests): test the execution of blocks + +## Conventions +All of the existing tests follow these conventions: +1. The `<file-name>.spec.ts` file is located next to the `<file-name>.ts` file itself. +2. The `*.jv` assets are located inside a `test/assets/<file-name>` folder. +Take a look at one of the exisiting tests for more details. + +## Grammar tests +These kind of tests are mainly located inside the [language-server](https://github.com/jvalue/jayvee/tree/main/libs/language-server) as well as the language parts of each extension (for example [std/lang](https://github.com/jvalue/jayvee/tree/main/libs/extensions/std/lang)). + +### Testing utils +The testing utils are located inside the `language-server` in a dedicated [test folder](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/test). +These utils can be imported using `@jvalue/jayvee-language-server/test` and contain the following parts: + +[**langium-utils.ts**](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/test/langium-utils.ts): +This utils file contains two functions: +- `parseHelper` to simplify parsing the input (content of a *.jv file) and returning the corresponding `LangiumDocument`, and +- `validationHelper` parse and validate the created document. +They are kept in a separate file due to being copied from the Langium repository and thus subject to a different code license and copyright. + +[**utils.ts**](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/test/utils.ts): +This file contains custom testing utility utils functions, like `readJvTestAssetHelper` for reading jv test assets. +Example: +``` ts +import * as path from 'path'; + +import { createJayveeServices } from '@jvalue/jayvee-language-server'; +import { + ParseHelperOptions, + expectNoParserAndLexerErrors, + parseHelper, + readJvTestAssetHelper, +} from '@jvalue/jayvee-language-server/test'; +import { AstNode, LangiumDocument } from 'langium'; + +describe('My example test', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../test/assets/', // localized path to test assets folder + ); + + beforeAll(() => { + // [...] register extensions etc + const services = createJayveeServices(NodeFileSystem).Jayvee; // Or retrieve them if services already exist + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + // [...] + + it('My dummy test', () => { + const text = readJvTestAsset('<sub-folder>/<test-asset-name>.jv'); + + const document = await parse(text); + expectNoParserAndLexerErrors(document); + // Rest of test + }); +}); +``` +If you want to simply validate the test assets, simply replace `parseHelper` with `validationHelper` (and adjust the types). +You can find detailed documentation of all the utility functions directly in the code. + +[**extension/**](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/test/extension): +This folder contains a Jayvee extension for testing. +If there are certain blocks required for testing a certain feature, they can be defined here. +One such example is the already defined `TestProperty` block which has a multitude of different properties, each with a different type. +This block is used for testing properties and property-assignments. +The extension provides loader and extractor blocks for all IOTypes without any properties. +These blocks are automatically generated at runtime with the following naming scheme: +`Test${ioType}${io === 'input' ? 'Loader' : 'Extractor'}` (Example: `TestFileExtractor`). +This allows for easy (grammar) testing of non loader/extractor blocks: +``` jv +pipeline Pipeline { + + TestExtractor -> BlockUnderTest -> TestLoader; + + block BlockUnderTest oftype CellWriter { + at: range A1:A3; + write: ['values', 'to', 'write']; + } + + block TestExtractor oftype TestSheetExtractor { } + block TestLoader oftype TestSheetLoader { } +} +``` + +### Existing tests +Currently there are already tests for the following parts: +- Language-server validation checks (located [here](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/lib/validation)) +- Language-server constraint validation (located [here](https://github.com/jvalue/jayvee/tree/dev/libs/language-server/src/lib/constraint)) +- Custom block (property) validation of the three existing extensions (std extension located [here](https://github.com/jvalue/jayvee/blob/dev/libs/extensions/std/lang/src)) +- Grammar validation tests for all official full examples from the [/example](https://github.com/jvalue/jayvee/tree/main/example) folder (located [here](https://github.com/jvalue/jayvee/blob/dev/libs/extensions/std/lang/src/example-validation.spec.ts)) +- Grammar validation tests for all block examples of the std extension (located [here](https://github.com/jvalue/jayvee/blob/dev/libs/extensions/std/lang/src/meta-inf-example-validation.spec.ts)) + +## Execution tests +These kind of tests are mainly located inside the [interpreter](https://github.com/jvalue/jayvee/tree/main/libs/language-server), the [interpreter-lib](https://github.com/jvalue/jayvee/tree/dev/libs/interpreter-lib), the [execution lib](https://github.com/jvalue/jayvee/tree/dev/libs/execution) as well as the execution parts of each extension (for example [std/exec](https://github.com/jvalue/jayvee/tree/main/libs/extensions/std/exec)). + +### Testing utils +The testing utils for execution tests are spread between the extensions, with the interfaces and base utils located inside the [execution lib](https://github.com/jvalue/jayvee/tree/dev/libs/execution). +They can be imported using `@jvalue/jayvee-extensions/rdbms/test`, `@jvalue/jayvee-extensions/std/test` and `@jvalue/jayvee-execution/test`. + +[**utils.ts**](https://github.com/jvalue/jayvee/blob/dev/libs/execution/test/utils.ts): +At the moment this only contains two functions: +- `clearBlockExecutorRegistry` for clearing the registry containing all `BlockExecutor`s, and +- `clearConstraintExecutorRegistry` clearing the corresponding `ConstraintExecutor`s registry. +They are required in case the tested method initializes Jayvee itself (see [smoke test](#existing-tests-1)). + +[**test-logger.ts**](https://github.com/jvalue/jayvee/blob/dev/libs/execution/test/test-logger.ts): +This contains a subclass of the [`DefaultLogger`](https://github.com/jvalue/jayvee/blob/dev/libs/execution/src/lib/logging/default-logger.ts) used for tests which require a `Logger` implementation. The `TestLogger` contains the following tests functionality: +- `getLogs`: retrieve the cached logs that the logger received. +- `clearLogs`: clear the cached logs. + +[**block-executor-mocks.ts**](https://github.com/jvalue/jayvee/blob/dev/libs/execution/test/block-executor-mock.ts): +`BlockExecutorMock` interface for defining mocks for `AbstractBlockExecutor`. Generally only loader and executor blocks require mocks, because they interact with "the outside world" (i.e. `HttpExtractor` making http calls). +Due to how vastly different each `BlockExecutor` can be, this interface is very simple, containing only a `setup(...args: unknown[])` and a `restore()` method. See below for existing implementations. + +[**rdbms/exec/test**](https://github.com/jvalue/jayvee/tree/dev/libs/extensions/rdbms/exec/test): +Contains the implementation of `BlockExecutorMock` for `PostgresLoaderExecutor` and `SQLiteLoaderExecutor`. +Both of these executors are mocked using `jest.mock` to mock the corresponding libraries (`pg` and `sqlite3`) +**Usage:** +``` ts +import { + PostgresLoaderExecutorMock, + SQLiteLoaderExecutorMock, +} from '@jvalue/jayvee-extensions/rdbms/test'; + +// Global mocking of external library at the top of test file required, +// even though the mocking is encapsulated in helper classes +jest.mock('pg', () => { + const mClient = { + connect: jest.fn(), + query: jest.fn(), + end: jest.fn(), + }; + return { Client: jest.fn(() => mClient) }; +}); +jest.mock('sqlite3', () => { + const mockDB = { + close: jest.fn(), + run: jest.fn(), + }; + return { Database: jest.fn(() => mockDB) }; +}); + +describe('Dummy describe', () => { + // [...] + + let postgresLoaderMock: PostgresLoaderExecutorMock; + let sqliteLoaderMock: SQLiteLoaderExecutorMock; + + beforeAll(() => { + postgresLoaderMock = new PostgresLoaderExecutorMock(); + sqliteLoaderMock = new SQLiteLoaderExecutorMock(); + }); + + afterEach(() => { + postgresLoaderMock.restore(); + sqliteLoaderMock.restore(); + }); + + it('Dummy test', async () => { + // Prepare mocks + postgresLoaderMock.setup(); + sqliteLoaderMock.setup(); + + // [...] execute test + + expect(postgresLoaderMock.pgClient.connect).toBeCalledTimes(1); + expect(postgresLoaderMock.pgClient.query).toBeCalledTimes(1); + expect(postgresLoaderMock.pgClient.end).toBeCalledTimes(1); + expect(sqliteLoaderMock.sqliteClient.run).toBeCalledTimes(1); + expect(sqliteLoaderMock.sqliteClient.close).toBeCalledTimes(1); + }); +}); +``` + +[**std/exec/test/mocks**](https://github.com/jvalue/jayvee/tree/dev/libs/extensions/std/exec/test): +Contains the implementation of `BlockExecutorMock` for `HttpExtractorExecutorMock`. +This implementation uses [nock](https://www.npmjs.com/package/nock) for mocking HTTP(S) responses. +The `setup` method is further specified requiring one parameter `registerMocks: () => Array<nock.Scope>`, which returns all used `nock.Scope` (i.e. the return value of `nock('<URL>')`), see usage below: +**Usage:** +``` ts +import * as path from 'path'; + +import { HttpExtractorExecutorMock } from '@jvalue/jayvee-extensions/std/test'; + +describe('Dummy describe', () => { + // [...] + + let httpExtractorMock: HttpExtractorExecutorMock; + + beforeAll(() => { + httpExtractorMock = new HttpExtractorExecutorMock(); + }); + + afterEach(() => { + httpExtractorMock.restore(); + }); + + it('should have no errors when executing gtfs-static-and-rt.jv example', async () => { + // Prepare mocks + httpExtractorMock.setup(() => { + return [ + nock( + '<URL_1>', + ) + .get('<PATH>') + .replyWithFile( + 200, + path.resolve(__dirname, '../test/assets/file1.zip'), + { + 'Content-Type': 'application/octet-stream', + }, + ), + nock('<URL_2>') + .get('<PATH_1>') + .replyWithFile( + 200, + path.resolve( + __dirname, + '../test/assets/file2', + ), + { + 'Content-Type': 'application/octet-stream', + }, + ) + .get('<PATH_2>') + .reply(200, { content: "My dummy json reply." }), + ]; + }) + + // [...] execute test + + expect(httpExtractorMock.nockScopes.every((scope) => scope.isDone())); + }); +}); +``` + +### Existing tests +Currently there are already tests for the following parts: +- Smoke test for official examples (located [here](https://github.com/jvalue/jayvee/blob/dev/apps/interpreter/src/examples-smoke-test.spec.ts)) diff --git a/apps/docs/versioned_docs/version-0.3.0/dev/12-jayvee-testing.md.license b/apps/docs/versioned_docs/version-0.3.0/dev/12-jayvee-testing.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/dev/12-jayvee-testing.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.3.0/user/block-types/ArchiveInterpreter.md b/apps/docs/versioned_docs/version-0.3.0/user/block-types/ArchiveInterpreter.md new file mode 100644 index 00000000..2a622e27 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/block-types/ArchiveInterpreter.md @@ -0,0 +1,33 @@ +--- +title: ArchiveInterpreter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `File` + +Output type: `FileSystem` + +## Description + +Interprets a `File` as an archive file and converts it to a `FileSystem`. The archive file root is considered the root of the `FileSystem`. + +## Example 1 + +```jayvee + block ZipArchiveInterpreter oftype ArchiveInterpreter { + archiveType: "zip"; + } +``` + +Interprets a `File` as a ZIP-archive and creates a `FileSystem` of its extracted contents. + +## Properties + +### `archiveType` + +Type `text` + +#### Description + +The archive type to be interpreted, e.g., "zip" or "gz". diff --git a/apps/docs/versioned_docs/version-0.3.0/user/block-types/ArchiveInterpreter.md.license b/apps/docs/versioned_docs/version-0.3.0/user/block-types/ArchiveInterpreter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/block-types/ArchiveInterpreter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.3.0/user/block-types/CSVInterpreter.md b/apps/docs/versioned_docs/version-0.3.0/user/block-types/CSVInterpreter.md new file mode 100644 index 00000000..9242e045 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/block-types/CSVInterpreter.md @@ -0,0 +1,55 @@ +--- +title: CSVInterpreter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `TextFile` + +Output type: `Sheet` + +## Description + +Interprets an input file as a csv-file containing string-values delimited by `delimiter` and outputs a `Sheet`. + +## Example 1 + +```jayvee + block AgencyCSVInterpreter oftype CSVInterpreter { + delimiter: ";"; + } +``` + +Interprets an input file as a csv-file containing string-values delimited by `;` and outputs `Sheet`. + +## Properties + +### `delimiter` + +Type `text` + +Default: `","` + +#### Description + +The delimiter for values in the CSV file. + +### `enclosing` + +Type `text` + +Default: `""` + +#### Description + +The enclosing character that may be used for values in the CSV file. + +### `enclosingEscape` + +Type `text` + +Default: `""` + +#### Description + +The character to escape enclosing characters in values. diff --git a/apps/docs/versioned_docs/version-0.3.0/user/block-types/CSVInterpreter.md.license b/apps/docs/versioned_docs/version-0.3.0/user/block-types/CSVInterpreter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/block-types/CSVInterpreter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.3.0/user/block-types/CellRangeSelector.md b/apps/docs/versioned_docs/version-0.3.0/user/block-types/CellRangeSelector.md new file mode 100644 index 00000000..2e55f64c --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/block-types/CellRangeSelector.md @@ -0,0 +1,33 @@ +--- +title: CellRangeSelector +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Sheet` + +Output type: `Sheet` + +## Description + +Selects a subset of a `Sheet` to produce a new `Sheet`. + +## Example 1 + +```jayvee + block CarsCoreDataSelector oftype CellRangeSelector { + select: range A1:E*; + } +``` + +Selects the cells in the given range and produces a new `Sheet` containing only the selected cells. + +## Properties + +### `select` + +Type `CellRange` + +#### Description + +The cell range to select. diff --git a/apps/docs/versioned_docs/version-0.3.0/user/block-types/CellRangeSelector.md.license b/apps/docs/versioned_docs/version-0.3.0/user/block-types/CellRangeSelector.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/block-types/CellRangeSelector.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.3.0/user/block-types/CellWriter.md b/apps/docs/versioned_docs/version-0.3.0/user/block-types/CellWriter.md new file mode 100644 index 00000000..e397dccf --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/block-types/CellWriter.md @@ -0,0 +1,53 @@ +--- +title: CellWriter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Sheet` + +Output type: `Sheet` + +## Description + +Writes textual values into cells of a `Sheet`. The number of text values needs to match the number of cells to write into. + +## Example 1 + +```jayvee + block NameHeaderWriter oftype CellWriter { + at: cell A1; + write: ["Name"]; + } +``` + +Write the value "Name" into cell `A1`. + +## Example 2 + +```jayvee + block HeaderSequenceWriter oftype CellWriter { + at: range A1:A2; + write: ["Name", "Age"]; + } +``` + +Write the values "Name", "Age" into cells `A1` and `A2`. + +## Properties + +### `write` + +Type `Collection<text>` + +#### Description + +The values to write. + +### `at` + +Type `CellRange` + +#### Description + +The cells to write into. diff --git a/apps/docs/versioned_docs/version-0.3.0/user/block-types/CellWriter.md.license b/apps/docs/versioned_docs/version-0.3.0/user/block-types/CellWriter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/block-types/CellWriter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.3.0/user/block-types/ColumnDeleter.md b/apps/docs/versioned_docs/version-0.3.0/user/block-types/ColumnDeleter.md new file mode 100644 index 00000000..affce276 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/block-types/ColumnDeleter.md @@ -0,0 +1,33 @@ +--- +title: ColumnDeleter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Sheet` + +Output type: `Sheet` + +## Description + +Deletes columns from a `Sheet`. Column IDs of subsequent columns will be shifted accordingly, so there will be no gaps. + +## Example 1 + +```jayvee + block MpgColumnDeleter oftype ColumnDeleter { + delete: [column B]; + } +``` + +Deletes column B (i.e. the second column). + +## Properties + +### `delete` + +Type `Collection<CellRange>` + +#### Description + +The columns to delete. Has to be a full column. diff --git a/apps/docs/versioned_docs/version-0.3.0/user/block-types/ColumnDeleter.md.license b/apps/docs/versioned_docs/version-0.3.0/user/block-types/ColumnDeleter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/block-types/ColumnDeleter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.3.0/user/block-types/FilePicker.md b/apps/docs/versioned_docs/version-0.3.0/user/block-types/FilePicker.md new file mode 100644 index 00000000..9fd0d5ad --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/block-types/FilePicker.md @@ -0,0 +1,33 @@ +--- +title: FilePicker +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `FileSystem` + +Output type: `File` + +## Description + +Selects one `File` from a `FileSystem` based on its relative path to the root of the `FileSystem`. If no file matches the relative path, no output is created and the execution of the pipeline is aborted. + +## Example 1 + +```jayvee + block AgencyFilePicker oftype FilePicker { + path: "./agency.txt"; + } +``` + +Tries to pick the file `agency.txt` from the root of the provided `FileSystem`. If `agency.txt` exists it is passed on as `File`, if it does not exist the execution of the pipeline is aborted. + +## Properties + +### `path` + +Type `text` + +#### Description + +The path of the file to select, relative to the root of the provided `FileSystem`. diff --git a/apps/docs/versioned_docs/version-0.3.0/user/block-types/FilePicker.md.license b/apps/docs/versioned_docs/version-0.3.0/user/block-types/FilePicker.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/block-types/FilePicker.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.3.0/user/block-types/GtfsRTInterpreter.md b/apps/docs/versioned_docs/version-0.3.0/user/block-types/GtfsRTInterpreter.md new file mode 100644 index 00000000..3da451b2 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/block-types/GtfsRTInterpreter.md @@ -0,0 +1,76 @@ +--- +title: GtfsRTInterpreter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `File` + +Output type: `Sheet` + +## Description + +Interprets an protobuf file (binary) of type `File` by decoding the file according to `gtfs-realtime.proto`. Outputs the extracted entity defined by `entity` as a `Sheet` + +## Example 1 + +```jayvee + block GtfsRTTripUpdateInterpreter oftype GtfsRTInterpreter{ + entity: "trip_update"; + } +``` + +A file is interpretet as an GTFS-RT file, which contains TripUpdate. + +## Properties + +### `entity` + +Type `text` + +#### Description + +Entity to process from GTFS-RT-feed (`trip_update`, `alert` or `vehicle`). + We currently support following Output-Sheets, each are an equivalent to the flattened Element Index defined in <https://developers.google.com/transit/gtfs-realtime/reference#element-index> (just required fields are included): + Entity TripUpdate: + ``` + [ + 'header.gtfs_realtime_version', + 'header.timestamp', + 'header.incrementality', + 'entity.id', + 'entity.trip_update.trip.trip_id', + 'entity.trip_update.trip.route_id', + 'entity.trip_update.stop_time_update.stop_sequence', + 'entity.trip_update.stop_time_update.stop_id', + 'entity.trip_update.stop_time_update.arrival.time', + 'entity.trip_update.stop_time_update.departure.time', + ]; + ``` + Entity VehiclePosition: + ``` + [ + 'header.gtfs_realtime_version', + 'header.timestamp', + 'header.incrementality', + 'entity.id', + 'entity.vehicle_position.vehicle_descriptor.id', + 'entity.vehicle_position.trip.trip_id', + 'entity.vehicle_position.trip.route_id', + 'entity.vehicle_position.position.latitude', + 'entity.vehicle_position.position.longitude', + 'entity.vehicle_position.timestamp', + ]; + ``` + Entity Alert: + ``` + [ + 'header.gtfs_realtime_version', + 'header.timestamp', + 'header.incrementality', + 'entity.id', + 'entity.alert.informed_entity.route_id', + 'entity.alert.header_text', + 'entity.alert.description_text', + ]; + ``` diff --git a/apps/docs/versioned_docs/version-0.3.0/user/block-types/GtfsRTInterpreter.md.license b/apps/docs/versioned_docs/version-0.3.0/user/block-types/GtfsRTInterpreter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/block-types/GtfsRTInterpreter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.3.0/user/block-types/HttpExtractor.md b/apps/docs/versioned_docs/version-0.3.0/user/block-types/HttpExtractor.md new file mode 100644 index 00000000..523e4eb3 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/block-types/HttpExtractor.md @@ -0,0 +1,73 @@ +--- +title: HttpExtractor +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `None` + +Output type: `File` + +## Description + +Extracts a `File` from the web. + +## Example 1 + +```jayvee + block CarsFileExtractor oftype HttpExtractor { + url: "tinyurl.com/4ub9spwz"; + } +``` + +Fetches a file from the given URL. + +## Properties + +### `url` + +Type `text` + +#### Description + +The URL to the file in the web to extract. + +### `retries` + +Type `integer` + +Default: `0` + +#### Description + +Configures how many retries should be executed after a failure fetching the data. + +### `retryBackoffMilliseconds` + +Type `integer` + +Default: `1000` + +#### Description + +Configures the wait time in milliseconds before executing a retry. + +### `retryBackoffStrategy` + +Type `text` + +Default: `"exponential"` + +#### Description + +Configures the wait strategy before executing a retry. Can have values "exponential" or "linear". + +### `followRedirects` + +Type `boolean` + +Default: `true` + +#### Description + +Indicates, whether to follow redirects on get requests. If `false`, redirects are not followed. Default `true` diff --git a/apps/docs/versioned_docs/version-0.3.0/user/block-types/HttpExtractor.md.license b/apps/docs/versioned_docs/version-0.3.0/user/block-types/HttpExtractor.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/block-types/HttpExtractor.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.3.0/user/block-types/PostgresLoader.md b/apps/docs/versioned_docs/version-0.3.0/user/block-types/PostgresLoader.md new file mode 100644 index 00000000..91782ab5 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/block-types/PostgresLoader.md @@ -0,0 +1,78 @@ +--- +title: PostgresLoader +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Table` + +Output type: `None` + +## Description + +Loads a `Table` into a PostgreSQL database sink. + +## Example 1 + +```jayvee + block CarsLoader oftype PostgresLoader { + host: "localhost"; + port: 5432; + username: "postgres"; + password: "postgres"; + database: "CarsDB"; + table: "Cars"; + } +``` + +A local Postgres instance is filled with table data about cars. + +## Properties + +### `host` + +Type `text` + +#### Description + +The hostname or IP address of the Postgres database. + +### `port` + +Type `integer` + +#### Description + +The port of the Postgres database. + +### `username` + +Type `text` + +#### Description + +The username to login to the Postgres database. + +### `password` + +Type `text` + +#### Description + +The password to login to the Postgres database. + +### `database` + +Type `text` + +#### Description + +The database to use. + +### `table` + +Type `text` + +#### Description + +The name of the table to write into. diff --git a/apps/docs/versioned_docs/version-0.3.0/user/block-types/PostgresLoader.md.license b/apps/docs/versioned_docs/version-0.3.0/user/block-types/PostgresLoader.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/block-types/PostgresLoader.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.3.0/user/block-types/RowDeleter.md b/apps/docs/versioned_docs/version-0.3.0/user/block-types/RowDeleter.md new file mode 100644 index 00000000..20093b18 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/block-types/RowDeleter.md @@ -0,0 +1,33 @@ +--- +title: RowDeleter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Sheet` + +Output type: `Sheet` + +## Description + +Deletes one or more rows from a `Sheet`. Row IDs of subsequent rows will be shifted accordingly, so there will be no gaps. + +## Example 1 + +```jayvee + block SecondRowDeleter oftype RowDeleter { + delete: [row 2]; + } +``` + +Deletes row 2 (i.e. the second row). + +## Properties + +### `delete` + +Type `Collection<CellRange>` + +#### Description + +The rows to delete. diff --git a/apps/docs/versioned_docs/version-0.3.0/user/block-types/RowDeleter.md.license b/apps/docs/versioned_docs/version-0.3.0/user/block-types/RowDeleter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/block-types/RowDeleter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.3.0/user/block-types/SQLiteLoader.md b/apps/docs/versioned_docs/version-0.3.0/user/block-types/SQLiteLoader.md new file mode 100644 index 00000000..d48e6f0f --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/block-types/SQLiteLoader.md @@ -0,0 +1,52 @@ +--- +title: SQLiteLoader +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Table` + +Output type: `None` + +## Description + +Loads a `Table` into a SQLite database sink. + +## Example 1 + +```jayvee + block CarsLoader oftype SQLiteLoader { + table: "cars"; + file: "./cars.db"; + } +``` + +A SQLite file `cars.db` is created in the working directory. Incoming data is written to the table `cars`. + +## Properties + +### `table` + +Type `text` + +#### Description + +The name of the table to write into. + +### `file` + +Type `text` + +#### Description + +The path to a SQLite file that will be created if it does not exist. Usual file extensions are `.sqlite` and `.db`. + +### `dropTable` + +Type `boolean` + +Default: `true` + +#### Description + +Indicates, whether to drop the table before loading data into it. If `false`, data is appended to the table instead of dropping it. diff --git a/apps/docs/versioned_docs/version-0.3.0/user/block-types/SQLiteLoader.md.license b/apps/docs/versioned_docs/version-0.3.0/user/block-types/SQLiteLoader.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/block-types/SQLiteLoader.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.3.0/user/block-types/SheetPicker.md b/apps/docs/versioned_docs/version-0.3.0/user/block-types/SheetPicker.md new file mode 100644 index 00000000..26710170 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/block-types/SheetPicker.md @@ -0,0 +1,33 @@ +--- +title: SheetPicker +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Workbook` + +Output type: `Sheet` + +## Description + +Selects one `Sheet` from a `Workbook` based on its `sheetName`. If no sheet matches the name, no output is created and the execution of the pipeline is aborted. + +## Example 1 + +```jayvee + block AgencySheetPicker oftype SheetPicker { + sheetName: "AgencyNames"; + } +``` + +Tries to pick the sheet `AgencyNames` from the provided `Workbook`. If `AgencyNames` exists it is passed on as `Sheet`, if it does not exist the execution of the pipeline is aborted. + +## Properties + +### `sheetName` + +Type `text` + +#### Description + +The name of the sheet to select. diff --git a/apps/docs/versioned_docs/version-0.3.0/user/block-types/SheetPicker.md.license b/apps/docs/versioned_docs/version-0.3.0/user/block-types/SheetPicker.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/block-types/SheetPicker.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.3.0/user/block-types/TableInterpreter.md b/apps/docs/versioned_docs/version-0.3.0/user/block-types/TableInterpreter.md new file mode 100644 index 00000000..7d81aae3 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/block-types/TableInterpreter.md @@ -0,0 +1,63 @@ +--- +title: TableInterpreter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Sheet` + +Output type: `Table` + +## Description + +Interprets a `Sheet` as a `Table`. In case a header row is present in the sheet, its names can be matched with the provided column names. Otherwise, the provided column names are assigned in order. + +## Example 1 + +```jayvee + block CarsTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "name" oftype text, + "mpg" oftype decimal, + "cyl" oftype integer, + ]; + } +``` + +Interprets a `Sheet` about cars with a topmost header row and interprets it as a `Table` by assigning a primitive valuetype to each column. The column names are matched to the header, so the order of the type assignments does not matter. + +## Example 2 + +```jayvee + block CarsTableInterpreter oftype TableInterpreter { + header: false; + columns: [ + "name" oftype text, + "mpg" oftype decimal, + "cyl" oftype integer, + ]; + } +``` + +Interprets a `Sheet` about cars without a topmost header row and interprets it as a `Table` by sequentially assigning a name and a primitive valuetype to each column of the sheet. Note that the order of columns matters here. The first column (column `A`) will be named "name", the second column (column `B`) will be named "mpg" etc. + +## Properties + +### `header` + +Type `boolean` + +Default: `true` + +#### Description + +Whether the first row should be interpreted as header row. + +### `columns` + +Type `Collection<ValuetypeAssignment>` + +#### Description + +Collection of valuetype assignments. Uses column names (potentially matched with the header or by sequence depending on the `header` property) to assign a primitive valuetype to each column. diff --git a/apps/docs/versioned_docs/version-0.3.0/user/block-types/TableInterpreter.md.license b/apps/docs/versioned_docs/version-0.3.0/user/block-types/TableInterpreter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/block-types/TableInterpreter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.3.0/user/block-types/TableTransformer.md b/apps/docs/versioned_docs/version-0.3.0/user/block-types/TableTransformer.md new file mode 100644 index 00000000..eb52dc90 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/block-types/TableTransformer.md @@ -0,0 +1,73 @@ +--- +title: TableTransformer +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Table` + +Output type: `Table` + +## Description + +Applies a transform on each value of a column. The input port type of the used transform has to match the type of the input column. + +## Example 1 + +```jayvee + transform CelsiusToFahrenheit { + from Celsius oftype decimal; + to Fahrenheit oftype decimal; + Fahrenheit: (Celsius * 9/5) + 32; + } + block CelsiusToFahrenheitTransformer oftype TableTransformer { + inputColumns: ['temperature']; + outputColumn: 'temperature'; + use: CelsiusToFahrenheit; + } +``` + +Given a column "temperature" with temperature values in Celsius, it overwrites the column with computed values in Fahrenheit by using the `CelsiusToFahrenheit` transform. The transform itself is defined elsewhere in the model. + +## Example 2 + +```jayvee + transform CelsiusToFahrenheit { + from Celsius oftype decimal; + to Fahrenheit oftype decimal; + Fahrenheit: (Celsius * 9/5) + 32; + } + block CelsiusToFahrenheitTransformer oftype TableTransformer { + inputColumns: ['temperatureCelsius']; + outputColumn: 'temperatureFahrenheit'; + use: CelsiusToFahrenheit; + } +``` + +Given a column "temperatureCelsius" with temperature values in Celsius, it adds a new column "temperatureFahrenheit" with computed values in Fahrenheit by using the `CelsiusToFahrenheit` transform. The transform itself is defined elsewhere in the model. + +## Properties + +### `inputColumns` + +Type `Collection<text>` + +#### Description + +The names of the input columns. The columns have to be present in the table and match with the transform's input port types. + +### `outputColumn` + +Type `text` + +#### Description + +The name of the output column. Overwrites the column if it already exists, or otherwise creates a new one. + +### `use` + +Type `Transform` + +#### Description + +Reference to the transform that is applied to the column. diff --git a/apps/docs/versioned_docs/version-0.3.0/user/block-types/TableTransformer.md.license b/apps/docs/versioned_docs/version-0.3.0/user/block-types/TableTransformer.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/block-types/TableTransformer.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.3.0/user/block-types/TextFileInterpreter.md b/apps/docs/versioned_docs/version-0.3.0/user/block-types/TextFileInterpreter.md new file mode 100644 index 00000000..0fefdcc2 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/block-types/TextFileInterpreter.md @@ -0,0 +1,35 @@ +--- +title: TextFileInterpreter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `File` + +Output type: `TextFile` + +## Description + +Interprets a `File` as a `TextFile`. + +## Properties + +### `encoding` + +Type `text` + +Default: `"utf-8"` + +#### Description + +The encoding used for decoding the file contents. + +### `lineBreak` + +Type `Regex` + +Default: `{}` + +#### Description + +The regex for identifying line breaks. diff --git a/apps/docs/versioned_docs/version-0.3.0/user/block-types/TextFileInterpreter.md.license b/apps/docs/versioned_docs/version-0.3.0/user/block-types/TextFileInterpreter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/block-types/TextFileInterpreter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.3.0/user/block-types/TextLineDeleter.md b/apps/docs/versioned_docs/version-0.3.0/user/block-types/TextLineDeleter.md new file mode 100644 index 00000000..b36a46c4 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/block-types/TextLineDeleter.md @@ -0,0 +1,23 @@ +--- +title: TextLineDeleter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `TextFile` + +Output type: `TextFile` + +## Description + +Deletes individual lines from a `TextFile`. + +## Properties + +### `lines` + +Type `Collection<integer>` + +#### Description + +The line numbers to delete. diff --git a/apps/docs/versioned_docs/version-0.3.0/user/block-types/TextLineDeleter.md.license b/apps/docs/versioned_docs/version-0.3.0/user/block-types/TextLineDeleter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/block-types/TextLineDeleter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.3.0/user/block-types/TextRangeSelector.md b/apps/docs/versioned_docs/version-0.3.0/user/block-types/TextRangeSelector.md new file mode 100644 index 00000000..75f5e68e --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/block-types/TextRangeSelector.md @@ -0,0 +1,36 @@ +--- +title: TextRangeSelector +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `TextFile` + +Output type: `TextFile` + +## Description + +Selects a range of lines from a `TextFile`. + +## Properties + +### `lineFrom` + +Type `integer` + +Default: `1` + +#### Description + +Inclusive beginning line number for the selection. + +### `lineTo` + +Type `integer` + +Default: `9007199254740991` + +#### Description + +Inclusive ending line number for the selection. + The default value is the biggest usable integer. diff --git a/apps/docs/versioned_docs/version-0.3.0/user/block-types/TextRangeSelector.md.license b/apps/docs/versioned_docs/version-0.3.0/user/block-types/TextRangeSelector.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/block-types/TextRangeSelector.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.3.0/user/block-types/XLSXInterpreter.md b/apps/docs/versioned_docs/version-0.3.0/user/block-types/XLSXInterpreter.md new file mode 100644 index 00000000..3dd9a3c9 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/block-types/XLSXInterpreter.md @@ -0,0 +1,23 @@ +--- +title: XLSXInterpreter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `File` + +Output type: `Workbook` + +## Description + +Interprets an input file as a XLSX-file and outputs a `Workbook` containing `Sheet`s. + +## Example 1 + +```jayvee + block AgencyXLSXInterpreter oftype XLSXInterpreter { } +``` + +Interprets an input file as a XLSX-file and outputs a `Workbook` containing `Sheet`s. + +## Properties diff --git a/apps/docs/versioned_docs/version-0.3.0/user/block-types/XLSXInterpreter.md.license b/apps/docs/versioned_docs/version-0.3.0/user/block-types/XLSXInterpreter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/block-types/XLSXInterpreter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.3.0/user/block-types/_category_.json b/apps/docs/versioned_docs/version-0.3.0/user/block-types/_category_.json new file mode 100644 index 00000000..740dd2f2 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/block-types/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Block Types", + "position": 3, + "link": { + "type": "generated-index", + "description": "These blocks are shipped with Jayvee and are available right out of the box." + } +} diff --git a/apps/docs/versioned_docs/version-0.3.0/user/block-types/_category_.json.license b/apps/docs/versioned_docs/version-0.3.0/user/block-types/_category_.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/block-types/_category_.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.3.0/user/composite-blocks.md b/apps/docs/versioned_docs/version-0.3.0/user/composite-blocks.md new file mode 100644 index 00000000..3db9e78f --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/composite-blocks.md @@ -0,0 +1,115 @@ +--- +sidebar_position: 4 +--- + +# Composite Blocks + +Composite blocks are a way to create new blocktypes in Jayvee by combining the functionality of existing blocks and pipes. By relying on composite blocks instead of implementing more builtin blocks in a language interpreter, Jayvee supports easy extension by users. + +Composite blocks define: +- with the `property` keyword: properties with a name and [value type](./core-concepts.md#valuetypes), optionally a default value +- with the `input` keyword: one input with a name and io type (that can be None) +- with the `output` keyword: one output with a name and io type (that can be None) +- one pipeline definition, starting from the input (using its name) and ending in the output (again using its name) +- all blocks that are used in the pipeline definition (either builtin or other composite blocks) + +## Example +As an example, the common use-case of extracting a CSV file from a webserver using HTTP. With builtin blocks, a pipeline would start with a HttpExtractor source that downloads a file from the internet and outputs a binary file. This file must be interpreted as text (using a TextFileInterpreter) and finally as Sheet (using a CSVInterpreter). + +### Implementation with builtin blocks +```mermaid +flowchart LR + A[HttpExtractor] --> B(TextFileInterpreter) + B --> C(CSVInterpreter) + C --> D(TableInterpreter) + D --> E[SQLiteSink] +``` + +A pipeline with builtin blocks is very verbose: + +```jayvee +pipeline CarsPipeline { + CarsExtractor + -> CarsTextFileInterpreter + -> CarsCSVInterpreter + -> CarsTableInterpreter + -> CarsLoader; + + block CarsExtractor oftype HttpExtractor { + url: "https://example.com/cars.csv"; + } + + block CarsTextFileInterpreter oftype TextFileInterpreter { } + + block CarsCSVInterpreter oftype CSVInterpreter { + enclosing: '"'; + } + // ... further block definitions +} +``` + +### Refactoring using composite blocks + +The common use-case of downloading a CSV file using HTTP can be refactored into a composite block. Note that we define all properties of the builtin blocks that are used as properties of the new CSVExtractor blocktype (but add fallback values). If some internal configuration is always the same, we could also not expose it as a property of the new blocktype. + +```jayvee +// Define a new blocktype named CSVExtractor outside of the pipeline +composite blocktype CSVExtractor { + // Properties of the CSVExtractor, some with default values + property url oftype text; + property delimiter oftype text: ','; + property enclosing oftype text: ''; + property enclosingEscape oftype text: ''; + + // Input and outputs + input inputName oftype None; + output outputName oftype Sheet; + + // Pipeline definition from input, over blocks defined later, to output + inputName + ->FileExtractor + ->FileTextInterpreter + ->FileCSVInterpreter + ->outputName; + + // Block definitions using values from properties by name + block FileExtractor oftype HttpExtractor { url: url; } + block FileTextInterpreter oftype TextFileInterpreter {} + + block FileCSVInterpreter oftype CSVInterpreter { + delimiter: delimiter; + enclosing: enclosing; + enclosingEscape: enclosingEscape; + } +} +``` + +With the new CSVExtractor composite blocktype, the pipeline now looks like this. + +```mermaid +flowchart LR + CSVExtractor --> D(TableInterpreter) + D --> E[SQLiteSink] + + subgraph CSVExtractor + A[HttpExtractor] --> B(TextFileInterpreter) + B --> C(CSVInterpreter) +end +``` + +If the CSVExtractor is available in the scope of the `CarsPipeline` from before (e.g., by defining it above the pipeline), it can then be used to shorten the actual pipeline code. + +```jayvee +pipeline CarsPipeline { + // HttpExtractor, TextFileInterpreter and CSVInterpreter have been replaced by CSVExtractor + CarsExtractor + -> CarsTableInterpreter + -> CarsLoader; + + block CarsExtractor oftype CSVExtractor { + url: "https://example.com/cars.csv"; + } + + // ... further block definitions +} +``` \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.3.0/user/composite-blocks.md.license b/apps/docs/versioned_docs/version-0.3.0/user/composite-blocks.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/composite-blocks.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.3.0/user/constraint-types/AllowlistConstraint.md b/apps/docs/versioned_docs/version-0.3.0/user/constraint-types/AllowlistConstraint.md new file mode 100644 index 00000000..f4710e00 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/constraint-types/AllowlistConstraint.md @@ -0,0 +1,27 @@ +--- +title: AllowlistConstraint +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Compatible ValueType: text + +## Description + +Limits the values to a defined a set of allowed values. Only values in the list are valid. + +## Example 1 + +```jayvee + constraint TimeUnitString oftype AllowlistConstraint { + allowlist: ["ms", "s", "min", "h", "d", "m", "y"]; + } +``` + +Only allows the common abbreviations for millisecond, second, minute, etc.. + +## Properties + +### `allowlist` + +Type `Collection<text>` diff --git a/apps/docs/versioned_docs/version-0.3.0/user/constraint-types/AllowlistConstraint.md.license b/apps/docs/versioned_docs/version-0.3.0/user/constraint-types/AllowlistConstraint.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/constraint-types/AllowlistConstraint.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.3.0/user/constraint-types/DenylistConstraint.md b/apps/docs/versioned_docs/version-0.3.0/user/constraint-types/DenylistConstraint.md new file mode 100644 index 00000000..91a3da32 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/constraint-types/DenylistConstraint.md @@ -0,0 +1,27 @@ +--- +title: DenylistConstraint +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Compatible ValueType: text + +## Description + +Defines a set of forbidden values. All values in the list are considered invalid. + +## Example 1 + +```jayvee + constraint NoPrimaryColors oftype DenylistConstraint { + denylist: ["red", "blue", "yellow"]; + } +``` + +Denies all primary colors. + +## Properties + +### `denylist` + +Type `Collection<text>` diff --git a/apps/docs/versioned_docs/version-0.3.0/user/constraint-types/DenylistConstraint.md.license b/apps/docs/versioned_docs/version-0.3.0/user/constraint-types/DenylistConstraint.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/constraint-types/DenylistConstraint.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.3.0/user/constraint-types/LengthConstraint.md b/apps/docs/versioned_docs/version-0.3.0/user/constraint-types/LengthConstraint.md new file mode 100644 index 00000000..8c4d3729 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/constraint-types/LengthConstraint.md @@ -0,0 +1,37 @@ +--- +title: LengthConstraint +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Compatible ValueType: text + +## Description + +Limits the length of a string with an upper and/or lower boundary. + Only values with a length within the given range are valid. + +## Example 1 + +```jayvee + constraint ShortAnswerConstraint oftype LengthConstraint { + minLength: 0; + maxLength: 20; + } +``` + +A text constraint with 0 to 20 characters. + +## Properties + +### `minLength` + +Type `integer` + +Default: `0` + +### `maxLength` + +Type `integer` + +Default: `9007199254740991` diff --git a/apps/docs/versioned_docs/version-0.3.0/user/constraint-types/LengthConstraint.md.license b/apps/docs/versioned_docs/version-0.3.0/user/constraint-types/LengthConstraint.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/constraint-types/LengthConstraint.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.3.0/user/constraint-types/RangeConstraint.md b/apps/docs/versioned_docs/version-0.3.0/user/constraint-types/RangeConstraint.md new file mode 100644 index 00000000..8b61b1e0 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/constraint-types/RangeConstraint.md @@ -0,0 +1,60 @@ +--- +title: RangeConstraint +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Compatible ValueType: decimal + +## Description + +Limits the range of a number value with an upper and/or lower boundary which can be inclusive or exclusive. Only values within the given range are considered valid. + +## Example 1 + +```jayvee + constraint HundredScale oftype RangeConstraint { + lowerBound: 1; + upperBound: 100; + } +``` + +A scale between (and including) 1 and 100. + +## Example 2 + +```jayvee + constraint HundredScale oftype RangeConstraint { + lowerBound: 1; + lowerBoundInclusive: false; + upperBound: 100; + } +``` + +A scale for numbers strictly larger than 1 and less or equal to 100. + +## Properties + +### `lowerBound` + +Type `decimal` + +Default: `-9007199254740991` + +### `lowerBoundInclusive` + +Type `boolean` + +Default: `true` + +### `upperBound` + +Type `decimal` + +Default: `9007199254740991` + +### `upperBoundInclusive` + +Type `boolean` + +Default: `true` diff --git a/apps/docs/versioned_docs/version-0.3.0/user/constraint-types/RangeConstraint.md.license b/apps/docs/versioned_docs/version-0.3.0/user/constraint-types/RangeConstraint.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/constraint-types/RangeConstraint.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.3.0/user/constraint-types/RegexConstraint.md b/apps/docs/versioned_docs/version-0.3.0/user/constraint-types/RegexConstraint.md new file mode 100644 index 00000000..b9b5c794 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/constraint-types/RegexConstraint.md @@ -0,0 +1,28 @@ +--- +title: RegexConstraint +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Compatible ValueType: text + +## Description + +Limits the values complying with a regex. + Only values that comply with the regex are considered valid. + +## Example 1 + +```jayvee + constraint IPv4Format oftype RegexConstraint { + regex: /^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$/; + } +``` + +Text that complies with the IPv4 address format. + +## Properties + +### `regex` + +Type `Regex` diff --git a/apps/docs/versioned_docs/version-0.3.0/user/constraint-types/RegexConstraint.md.license b/apps/docs/versioned_docs/version-0.3.0/user/constraint-types/RegexConstraint.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/constraint-types/RegexConstraint.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.3.0/user/constraint-types/_category_.json b/apps/docs/versioned_docs/version-0.3.0/user/constraint-types/_category_.json new file mode 100644 index 00000000..cd8b1300 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/constraint-types/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Constraint Types", + "position": 6, + "link": { + "type": "generated-index", + "description": "These constraints are shipped with Jayvee and are available right out of the box." + } +} diff --git a/apps/docs/versioned_docs/version-0.3.0/user/constraint-types/_category_.json.license b/apps/docs/versioned_docs/version-0.3.0/user/constraint-types/_category_.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/constraint-types/_category_.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.3.0/user/core-concepts.md b/apps/docs/versioned_docs/version-0.3.0/user/core-concepts.md new file mode 100644 index 00000000..add0a01f --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/core-concepts.md @@ -0,0 +1,89 @@ +--- +sidebar_position: 2 +--- + +# Core Concepts + +The core concepts of Jayvee are `Pipelines`, `Blocks`, and `ValueTypes`. + +## Pipelines + +A `Pipeline` is a sequence of different computing steps, the `Blocks`. +The default output of a block becomes the default input of the next block, building a chain of computing steps. +In the scope of a `Pipeline`, you can connect these blocks via the `pipe` syntax: + +```jayvee +pipeline CarsPipeline { + // Assumption: blocks "GasReserveHttpExtractor", "GasReserveCSVInterpreter", "GasReserveTableInterpreter", and "GasReserveLoader" are defined + + GasReserveHttpExtractor + -> GasReserveTextFileInterpreter + -> GasReserveCSVInterpreter + -> GasReserveTableInterpreter + -> GasReserveLoader; +} +``` + +## Blocks + +A `Block` is a processing step within a `Pipeline`. +It can have a default input and a default output. +We differentiate the following types of `Blocks`: +- `ExtractorBlocks` do not have a default input but only a default output. They model a **data source**. +- `TransformatorBlocks` have a default input and a default output. They model a **transformation**. +- `LoaderBlocks` do have a default input but nor a default output. They model a **data sink**. + +The general structure of a `Pipeline` consisting of different blocks is the following: + +```mermaid +flowchart LR + A[ExtractorBlock] --> B(TransformatorBlock) + B --> C(TransformatorBlock) + C --> D(LoaderBlock) +``` + +The common syntax of blocks is at its core a key-value map to provide configuration to the block. +The availability of property keys and their respective `ValueTypes` is determined by the type of the `Block` - indicated by the identifier after the keyword `oftype`: + +```jayvee +block GasReserveHttpExtractor oftype HttpExtractor { + // key: value + url: "https://www.bundesnetzagentur.de/_tools/SVG/js2/_functions/csv_export.html?view=renderCSV&id=1089590"; +} +``` + +In the example above, the `url` property of type `text` is defined by the corresponding `HttpExtractor` block type. + +Blocks can be either defined as part of the language, called `builtin` or defined as composition of existing blocks by users in Jayvee, called `composite`. See the documentation for [Composite Blocks](./composite-blocks.md). + +## ValueTypes + +A `ValueType` is the definition of a data type of the processed data. +Some `Blocks` use `ValueTypes` to define logic (like filtering or assessing the data type in a data sink). +We differentiate the following types of `ValueTypes`: +- `Built-in ValueTypes` come with the basic version of Jayvee. See [Built-in Valuetypes](./valuetypes/builtin-valuetypes). +- `Primitive ValueTypes` can be defined by the user to model domain-specific data types and represent a single value. + `Constraints` can be added to a `Primitive ValueType`. +See [Primitive Valuetypes](./valuetypes/primitive-valuetypes). +- `Compound ValueTypes`: UPCOMING. + +```jayvee +valuetype GasFillLevel oftype integer { + constraints: [ GasFillLevelRange ]; +} + +constraint GasFillLevelRange on decimal: + value >= 0 and value <= 100; +``` + +## Transforms +`Transforms` are used to transform data from one `ValueType` to a different one. For more details, see [Transforms](./transforms.md). + +```jayvee +transform CelsiusToKelvin { + from tempCelsius oftype decimal; + to tempKelvin oftype decimal; + + tempKelvin: tempCelsius + 273.15; +} +``` \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.3.0/user/core-concepts.md.license b/apps/docs/versioned_docs/version-0.3.0/user/core-concepts.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/core-concepts.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.3.0/user/examples/_category_.json b/apps/docs/versioned_docs/version-0.3.0/user/examples/_category_.json new file mode 100644 index 00000000..65abd42e --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/examples/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Jayvee Examples", + "position": 10, + "link": { + "type": "generated-index", + "description": "Examples of Jayvee models" + } +} diff --git a/apps/docs/versioned_docs/version-0.3.0/user/examples/_category_.json.license b/apps/docs/versioned_docs/version-0.3.0/user/examples/_category_.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/examples/_category_.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.3.0/user/examples/cars.md b/apps/docs/versioned_docs/version-0.3.0/user/examples/cars.md new file mode 100644 index 00000000..ea96dbfa --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/examples/cars.md @@ -0,0 +1,110 @@ +--- +title: cars +--- + +```jayvee +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// Example 1: Cars +// Learning goals: +// - Understand the core concepts pipeline, block, and pipe +// - Understand the general structure of a pipeline + +// 1. This Jayvee model describes a pipeline +// from a CSV file in the web +// to a SQLite file sink. +pipeline CarsPipeline { + + // 2. We describe the structure of the pipeline, + // usually at the top of the pipeline. + // by connecting blocks via pipes. + + // 3. Syntax of a pipe + // connecting the block CarsExtractor + // with the block CarsTextFileInterpreter. + CarsExtractor -> CarsTextFileInterpreter; + + // 4. The output of the preceding block is hereby used + // as input for the succeeding block. + + // 5. Pipes can be further chained, + // leading to an overview of the pipeline. + CarsTextFileInterpreter + -> CarsCSVInterpreter + -> NameHeaderWriter + -> CarsTableInterpreter + -> CarsLoader; + + + // 6. Below the pipes, we usually define the blocks + // that are connected by the pipes. + + // 7. Blocks instantiate a blocktype by using the oftype keyword. + // The blocktype defines the available properties that the block + // can use to specify the intended behavior of the block + block CarsExtractor oftype HttpExtractor { + + // 8. Properties are assigned to concrete values. + // Here, we specify the URL where the file shall be downloaded from. + url: "https://gist.githubusercontent.com/noamross/e5d3e859aa0c794be10b/raw/b999fb4425b54c63cab088c0ce2c0d6ce961a563/cars.csv"; + } + + // 9. The HttpExtractor requires no input and produces a binary file as output. + // This file has to be interpreted, e.g., as text file. + block CarsTextFileInterpreter oftype TextFileInterpreter { } + + // 10. Next, we interpret the text file as sheet. + // A sheet only contains text cells and is useful for manipulating the shape of data before assigning more strict value types to cells. + block CarsCSVInterpreter oftype CSVInterpreter { + enclosing: '"'; + } + + // 11. We can write into cells of a sheet using the CellWriter blocktype. + block NameHeaderWriter oftype CellWriter { + // 12. We utilize a syntax similar to spreadsheet programs. + // Cell ranges can be described using the keywords "cell", "row", "column", or "range" that indicate which + // cells are selected for the write action. + at: cell A1; + + // 13. For each cell we selected with the "at" property above, + // we can specify what value shall be written into the cell. + write: ["name"]; + } + + // 14. As a next step, we interpret the sheet as a table by adding structure. + // We define a valuetype per column that specifies the data type of the column. + // Rows that include values that are not valid according to the their valuetypes are dropped automatically. + block CarsTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "name" oftype text, + "mpg" oftype decimal, + "cyl" oftype integer, + "disp" oftype decimal, + "hp" oftype integer, + "drat" oftype decimal, + "wt" oftype decimal, + "qsec" oftype decimal, + "vs" oftype integer, + "am" oftype integer, + "gear" oftype integer, + "carb" oftype integer + ]; + } + + // 15. As a last step, we load the table into a sink, + // here into a sqlite file. + // The structural information of the table is used + // to generate the correct table. + block CarsLoader oftype SQLiteLoader { + table: "Cars"; + file: "./cars.sqlite"; + } + + // 16. Congratulations! + // You can now use the sink for your data analysis, app, + // or whatever you want to do with the cleaned data. +} +``` \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.3.0/user/examples/cars.md.license b/apps/docs/versioned_docs/version-0.3.0/user/examples/cars.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/examples/cars.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.3.0/user/examples/electric-vehicles.md b/apps/docs/versioned_docs/version-0.3.0/user/examples/electric-vehicles.md new file mode 100644 index 00000000..99398633 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/examples/electric-vehicles.md @@ -0,0 +1,150 @@ +--- +title: electric-vehicles +--- + +```jayvee +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// Example 2: Electric Vehicles +// Learning goals: +// - Understand further core concepts transforms and valuetypes +// - Understand how to construct a pipeline with multiple sinks +// - Understand the use of runtime parameters + +// 1. This Jayvee model describes a pipeline +// from a CSV file in the web +// to a SQLite file and a PostgreSQL db sink. +pipeline ElectricVehiclesPipeline { + // See here for meta-data of the data source + // https://catalog.data.gov/dataset/electric-vehicle-population-data/resource/fa51be35-691f-45d2-9f3e-535877965e69 + + // 2. At the top of a pipeline, we describe the + // structure of the pipeline. The first part until + // the ElectricRangeTransformer is a linear sequence + // of blocks. From there we can see a split into two + // parallel sequences that load the data in to two + // different sinks. + ElectricVehiclesHttpExtractor + -> ElectricVehiclesTextFileInterpreter + -> ElectricVehiclesCSVInterpreter + -> ElectricVehiclesTableInterpreter + -> ElectricRangeTransformer; + + ElectricRangeTransformer + -> ElectricVehiclesSQLiteLoader; + + ElectricRangeTransformer + -> ElectricVehiclesPostgresLoader; + + // 3. After the pipeline structure, we define the blocks used. + block ElectricVehiclesHttpExtractor oftype HttpExtractor { + url: "https://data.wa.gov/api/views/f6w7-q2d2/rows.csv?accessType=DOWNLOAD"; + } + + block ElectricVehiclesTextFileInterpreter oftype TextFileInterpreter { } + + block ElectricVehiclesCSVInterpreter oftype CSVInterpreter { } + + block ElectricVehiclesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + // 4. Here, a user-deifned valuetype is used to describe this column. + // The capital letter indicates that the valuetype is not builtin + // by convention. The valuetype itself is defined further below. + "VIN (1-10)" oftype VehicleIdentificationNumber10, + "County" oftype text, + "City" oftype text, + "State" oftype UsStateCode, + "Postal Code" oftype text, + "Model Year" oftype integer, + "Make" oftype text, + "Model" oftype text, + "Electric Vehicle Type" oftype text, + "Clean Alternative Fuel Vehicle (CAFV) Eligibility" oftype text, + "Electric Range" oftype integer, + "Base MSRP" oftype integer, + "Legislative District" oftype text, + "DOL Vehicle ID" oftype integer, + "Vehicle Location" oftype text, + "Electric Utility" oftype text, + "2020 Census Tract" oftype text, + ]; + } + + // 5. This block describes the application of a transform function + // taking a column as input and adding another computed column. + // The applied transform function is defined below and referenced + // by the "use" property. + block ElectricRangeTransformer oftype TableTransformer { + inputColumns: ["Electric Range"]; + outputColumn: "Electric Range (km)"; + use: MilesToKilometers; + } + + // 6. Here, we define a transform function, taking parameters + // as input ("from" keyword), and producing an output ("to" keyword). + // Inputs and outputs have to be further described by a valuetype. + transform MilesToKilometers { + from miles oftype decimal; + to kilometers oftype integer; + + // 7. In order to express what the transform function does, + // we assign an expression to the output. Values from the input and output of the transform can be referred to by name. + kilometers: round (miles * 1.609344); + } + + block ElectricVehiclesSQLiteLoader oftype SQLiteLoader { + table: "ElectricVehiclePopulationData"; + file: "./electric-vehicles.sqlite"; + } + + block ElectricVehiclesPostgresLoader oftype PostgresLoader { + // 8. The requires keyword allows us to define runtime parameters. + // These values have to be provided as environment variables when interpreting the Jayvee model. + host: requires DB_HOST; + port: requires DB_PORT; + username: requires DB_USERNAME; + password: requires DB_PASSWORD; + database: requires DB_DATABASE; + table: "ElectricVehiclePopulationData"; + } +} + +// 9. Below the pipeline, we model user-define valuetypes. +// We give them a speaking name and provide a base valuetype +// that this valuetype builts on. User-defined valuetypes always place additional constraints on existing valuetypes. +valuetype VehicleIdentificationNumber10 oftype text { + // 10. Valuetypes can be further refined by providing constraints. + constraints: [ + OnlyCapitalLettersAndDigits, + ExactlyTenCharacters, + ]; +} + +// 11. This constraint works on text valuetypes and requires values +// to match a given regular expression in order to be valid. +constraint OnlyCapitalLettersAndDigits on text: + value matches /^[A-Z0-9]*$/; + +constraint ExactlyTenCharacters on text: + value.length == 10; + +valuetype UsStateCode oftype text { + constraints: [ + UsStateCodeAllowlist, + ]; +} + +constraint UsStateCodeAllowlist on text: + value in [ + "AL", "AK", "AZ", "AR", "AS", "CA", "CO", "CT", "DE", "DC", + "FL", "GA", "GU", "HI", "ID", "IL", "IN", "IA", "KS", "KY", + "LA", "ME", "MD", "MA", "MI", "MN", "MS", "MO", "MT", "NE", + "NV", "NH", "NJ", "NM", "NY", "NC", "ND", "MP", "OH", "OK", + "OR", "PA", "PR", "RI", "SC", "SD", "TN", "TX", "TT", "UT", + "VT", "VA", "VI", "WA", "WV", "WI", "WY", + ]; + +``` \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.3.0/user/examples/electric-vehicles.md.license b/apps/docs/versioned_docs/version-0.3.0/user/examples/electric-vehicles.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/examples/electric-vehicles.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.3.0/user/examples/gtfs-rt.md b/apps/docs/versioned_docs/version-0.3.0/user/examples/gtfs-rt.md new file mode 100644 index 00000000..7610d8c1 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/examples/gtfs-rt.md @@ -0,0 +1,133 @@ +--- +title: gtfs-rt +--- + +```jayvee +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// Example 3: GTFS Realtime Data +// Learning goals: +// - Understand the construction of a csv file with multiple tables +// - Understand how to work with live data + +// 1. This Jayvee model describes a pipeline +// from a GTFS RT data source in the web +// to a SQLite file with multiple tables. +pipeline GtfsRTSimplePipeline { + + // 2. As you can see here, we have three independent + // sequences of pipes in this pipeline. + GTFSRTTripUpdateFeedExtractor + ->GtfsRTTripUpdateInterpreter + ->TripUpdateTableInterpreter + ->TripUpdateLoader; + + GTFSRTVehiclePositionFeedExtractor + ->GtfsRTVehiclePositionInterpreter + ->VehiclePositionTableInterpreter + ->VehicleLoader; + + GTFSRTAlertFeedExtractor + ->GtfsRTAlertInterpreter + ->AlertTableInterpreter + ->AlertLoader; + + // 3. We define a series of HttpExtractors that each pull data + // from an HTTP endpoint + block GTFSRTTripUpdateFeedExtractor oftype HttpExtractor { + url: "https://proxy.transport.data.gouv.fr/resource/bibus-brest-gtfs-rt-trip-update"; + } + + block GTFSRTVehiclePositionFeedExtractor oftype HttpExtractor { + url: "https://proxy.transport.data.gouv.fr/resource/bibus-brest-gtfs-rt-vehicle-position"; + } + + block GTFSRTAlertFeedExtractor oftype HttpExtractor { + url: "https://proxy.transport.data.gouv.fr/resource/bibus-brest-gtfs-rt-alerts"; + } + + // 4. In the next step, we use the domain-specific GtfsRTInterpreter + // to interpret the fetched files as sheets + block GtfsRTTripUpdateInterpreter oftype GtfsRTInterpreter { + entity: "trip_update"; + } + + block GtfsRTAlertInterpreter oftype GtfsRTInterpreter { + entity: "alert"; + } + + block GtfsRTVehiclePositionInterpreter oftype GtfsRTInterpreter { + entity: "vehicle"; + } + + // 5. Next, we interpret the sheets as tables + block TripUpdateTableInterpreter oftype TableInterpreter { + header: true; + columns:[ + "header.gtfs_realtime_version" oftype text, + "header.timestamp" oftype text, + "header.incrementality" oftype text, + "entity.id" oftype text, + "entity.trip_update.trip.trip_id" oftype text, + "entity.trip_update.trip.route_id" oftype text, + "entity.trip_update.stop_time_update.stop_sequence" oftype text, + "entity.trip_update.stop_time_update.stop_id" oftype text, + "entity.trip_update.stop_time_update.arrival.time" oftype text, + "entity.trip_update.stop_time_update.departure.time" oftype text, + ]; + } + + block VehiclePositionTableInterpreter oftype TableInterpreter { + header: true; + columns:[ + "header.gtfs_realtime_version" oftype text, + "header.timestamp" oftype text, + "header.incrementality" oftype text, + "entity.id" oftype text, + "entity.vehicle_position.vehicle_descriptor.id" oftype text, + "entity.vehicle_position.trip.trip_id" oftype text, + "entity.vehicle_position.trip.route_id" oftype text, + "entity.vehicle_position.position.latitude" oftype text, + "entity.vehicle_position.position.longitude" oftype text, + "entity.vehicle_position.timestamp" oftype text + ]; + } + + block AlertTableInterpreter oftype TableInterpreter { + header: true; + columns:[ + 'header.gtfs_realtime_version' oftype text, + 'header.timestamp' oftype text, + 'header.incrementality' oftype text, + 'entity.id' oftype text, + 'entity.alert.informed_entity.route_id' oftype text, + 'entity.alert.header_text' oftype text, + 'entity.alert.description_text' oftype text, + ]; + } + + // 6. Last, we load the tables into the same SQLite file. + // Each loader has to define a different table name. + // For working with live data, we use the property "dropTable: false" + // to append data instead of deleting the previous data. + block TripUpdateLoader oftype SQLiteLoader { + table: "gtfs-rt-trip_update"; + file: "./gtfs.sqlite"; + dropTable: false; + } + + block VehicleLoader oftype SQLiteLoader { + table: "gtfs-rt-vehicle_position"; + file: "./gtfs.sqlite"; + dropTable: false; + } + + block AlertLoader oftype SQLiteLoader { + table: "gtfs-rt-alert"; + file: "./gtfs.sqlite"; + dropTable: false; + } +} +``` \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.3.0/user/examples/gtfs-rt.md.license b/apps/docs/versioned_docs/version-0.3.0/user/examples/gtfs-rt.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/examples/gtfs-rt.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.3.0/user/examples/gtfs-static.md b/apps/docs/versioned_docs/version-0.3.0/user/examples/gtfs-static.md new file mode 100644 index 00000000..ca6434df --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/examples/gtfs-static.md @@ -0,0 +1,370 @@ +--- +title: gtfs-static +--- + +```jayvee +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// Example 4: GTFS Static Data +// Learning goals: +// - Understand how to work with file systems + +// 1. This Jayvee model describes a pipeline +// from a zip file in the GTFS format in the web +// to a joint SQLite file with multiple tables. +pipeline GtfsPipeline { + + // 2. The origin for multiple pipe sequences is a zip + // file. Each csv file in this zip is further processed + // by its own sequence of blocks and pipes. + GTFSSampleFeedExtractor -> ZipArchiveInterpreter; + + ZipArchiveInterpreter + -> AgencyFilePicker + -> AgencyTextFileInterpreter + -> AgencyCSVInterpreter + -> AgencyTableInterpreter + -> AgencyLoader; + + ZipArchiveInterpreter + -> CalendarDatesFilePicker + -> CalendarDatesTextFileInterpreter + -> CalendarDatesCSVInterpreter + -> CalendarDatesTableInterpreter + -> CalendarDatesLoader; + + ZipArchiveInterpreter + -> CalendarFilePicker + -> CalendarTextFileInterpreter + -> CalendarCSVInterpreter + -> CalendarTableInterpreter + -> CalendarLoader; + + ZipArchiveInterpreter + -> FareAttributesFilePicker + -> FareAttributesTextFileInterpreter + -> FareAttributesCSVInterpreter + -> FareAttributesTableInterpreter + -> FareAttributesLoader; + + ZipArchiveInterpreter + -> FareRulesFilePicker + -> FareRulesTextFileInterpreter + -> FareRulesCSVInterpreter + -> FareRulesTableInterpreter + -> FareRulesLoader; + + ZipArchiveInterpreter + -> FrequenciesFilePicker + -> FrequenciesTextFileInterpreter + -> FrequenciesCSVInterpreter + -> FrequenciesTableInterpreter + -> FrequenciesLoader; + + ZipArchiveInterpreter + -> RoutesFilePicker + -> RoutesTextFileInterpreter + -> RoutesCSVInterpreter + -> RoutesTableInterpreter + -> RoutesLoader; + + ZipArchiveInterpreter + -> ShapesFilePicker + -> ShapesTextFileInterpreter + -> ShapesCSVInterpreter + -> ShapesTableInterpreter + -> ShapesLoader; + + ZipArchiveInterpreter + -> StopTimesFilePicker + -> StopTimesTextFileInterpreter + -> StopTimesCSVInterpreter + -> StopTimesTableInterpreter + -> StopTimesLoader; + + ZipArchiveInterpreter + -> StopsFilePicker + -> StopsTextFileInterpreter + -> StopsCSVInterpreter + -> StopsTableInterpreter + -> StopsLoader; + + ZipArchiveInterpreter + -> TripsFilePicker + -> TripsTextFileInterpreter + -> TripsCSVInterpreter + -> TripsTableInterpreter + -> TripsLoader; + + // 3. As a first step, we download the zip file and interpret it. + block GTFSSampleFeedExtractor oftype HttpExtractor { + url: "https://developers.google.com/static/transit/gtfs/examples/sample-feed.zip"; + } + + block ZipArchiveInterpreter oftype ArchiveInterpreter { + archiveType: "zip"; + } + + // 4. Next, we pick several csv files (with the file extension ".txt") + // for further processing . + block AgencyFilePicker oftype FilePicker { + path: "/agency.txt"; + } + + block CalendarDatesFilePicker oftype FilePicker { + path: "/calendar_dates.txt"; + } + + block CalendarFilePicker oftype FilePicker { + path: "/calendar.txt"; + } + + block FareAttributesFilePicker oftype FilePicker { + path: "/fare_attributes.txt"; + } + + block FareRulesFilePicker oftype FilePicker { + path: "/fare_rules.txt"; + } + + block FrequenciesFilePicker oftype FilePicker { + path: "/frequencies.txt"; + } + + block RoutesFilePicker oftype FilePicker { + path: "/routes.txt"; + } + + block ShapesFilePicker oftype FilePicker { + path: "/shapes.txt"; + } + + block StopTimesFilePicker oftype FilePicker { + path: "/stop_times.txt"; + } + + block StopsFilePicker oftype FilePicker { + path: "/stops.txt"; + } + + block TripsFilePicker oftype FilePicker { + path: "/trips.txt"; + } + + // 5. The rest of the pipeline follows the usual pattern. + block AgencyTextFileInterpreter oftype TextFileInterpreter { } + block CalendarDatesTextFileInterpreter oftype TextFileInterpreter { } + block CalendarTextFileInterpreter oftype TextFileInterpreter { } + block FareAttributesTextFileInterpreter oftype TextFileInterpreter { } + block FareRulesTextFileInterpreter oftype TextFileInterpreter { } + block FrequenciesTextFileInterpreter oftype TextFileInterpreter { } + block RoutesTextFileInterpreter oftype TextFileInterpreter { } + block ShapesTextFileInterpreter oftype TextFileInterpreter { } + block StopTimesTextFileInterpreter oftype TextFileInterpreter { } + block StopsTextFileInterpreter oftype TextFileInterpreter { } + block TripsTextFileInterpreter oftype TextFileInterpreter { } + block AgencyCSVInterpreter oftype CSVInterpreter { } + block CalendarDatesCSVInterpreter oftype CSVInterpreter { } + block CalendarCSVInterpreter oftype CSVInterpreter { } + block FareAttributesCSVInterpreter oftype CSVInterpreter { } + block FareRulesCSVInterpreter oftype CSVInterpreter { } + block FrequenciesCSVInterpreter oftype CSVInterpreter { } + block RoutesCSVInterpreter oftype CSVInterpreter { } + block ShapesCSVInterpreter oftype CSVInterpreter { } + block StopTimesCSVInterpreter oftype CSVInterpreter { } + block StopsCSVInterpreter oftype CSVInterpreter { } + block TripsCSVInterpreter oftype CSVInterpreter { } + + block AgencyTableInterpreter oftype TableInterpreter { + header: true; + columns:[ + "agency_id" oftype text, //Conditional columns are considered as required + "agency_name" oftype text, + "agency_url" oftype text, + "agency_timezone" oftype text + ]; + } + + block CalendarDatesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "service_id" oftype text, + "date" oftype text, + "exception_type" oftype text + ]; + } + + block CalendarTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "service_id" oftype text, + "monday" oftype text, + "tuesday" oftype text, + "wednesday" oftype text, + "thursday" oftype text, + "friday" oftype text, + "saturday" oftype text, + "sunday" oftype text, + "start_date" oftype text, + "end_date" oftype text + ]; + } + + block FareAttributesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "fare_id" oftype text, + "price" oftype text, + "currency_type" oftype text, + "payment_method" oftype text, + "transfers" oftype text, + "transfer_duration" oftype text + ]; + } + + block FareRulesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "fare_id" oftype text, + "route_id" oftype text, + "origin_id" oftype text, + "destination_id" oftype text, + "contains_id" oftype text + ]; + } + + block FrequenciesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "trip_id" oftype text, + "start_time" oftype text, + "end_time" oftype text, + "headway_secs" oftype text + ]; + } + + block RoutesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "route_id" oftype text, + "agency_id" oftype text, + "route_short_name" oftype text, + "route_long_name" oftype text, + "route_desc" oftype text, + "route_type" oftype text, + "route_url" oftype text, + "route_color" oftype text, + "route_text_color" oftype text + ]; + } + + block ShapesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "shape_id" oftype text, + "shape_pt_lat" oftype text, + "shape_pt_lon" oftype text, + "shape_pt_sequence" oftype text, + "shape_dist_traveled" oftype text + ]; + } + + block StopTimesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "trip_id" oftype text, + "arrival_time" oftype text, + "departure_time" oftype text, + "stop_id" oftype text, + "stop_sequence" oftype text, + "stop_headsign" oftype text, + "pickup_type" oftype text, + "drop_off_time" oftype text, + "shape_dist_traveled" oftype text + ]; + } + + block StopsTableInterpreter oftype TableInterpreter { + header: true; + columns:[ + "stop_id" oftype text, + "stop_name" oftype text, + "stop_desc" oftype text, + "stop_lat" oftype text, + "stop_lon" oftype text, + "zone_id" oftype text, + "stop_url" oftype text + ]; + } + + block TripsTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "route_id" oftype text, + "service_id" oftype text, + "trip_id" oftype text, + "trip_headsign" oftype text, + "direction_id" oftype text, + "block_id" oftype text, + "shape_id" oftype text + ]; + } + + block AgencyLoader oftype SQLiteLoader { + table: "agency"; + file: "./gtfs.sqlite"; + } + + block CalendarDatesLoader oftype SQLiteLoader { + table: "calendar_dates"; + file: "./gtfs.sqlite"; + } + + block CalendarLoader oftype SQLiteLoader { + table: "calendar"; + file: "./gtfs.sqlite"; + } + + block FareAttributesLoader oftype SQLiteLoader { + table: "fare_attributes"; + file: "./gtfs.sqlite"; + } + + block FareRulesLoader oftype SQLiteLoader { + table: "fare_rules"; + file: "./gtfs.sqlite"; + } + + block FrequenciesLoader oftype SQLiteLoader { + table: "frequencies"; + file: "./gtfs.sqlite"; + } + + block RoutesLoader oftype SQLiteLoader { + table: "routes"; + file: "./gtfs.sqlite"; + } + + block ShapesLoader oftype SQLiteLoader { + table: "shapes"; + file: "./gtfs.sqlite"; + } + + block StopTimesLoader oftype SQLiteLoader { + table: "stop_times"; + file: "./gtfs.sqlite"; + } + + block StopsLoader oftype SQLiteLoader { + table: "stops"; + file: "./gtfs.sqlite"; + } + + block TripsLoader oftype SQLiteLoader { + table: "trips"; + file: "./gtfs.sqlite"; + } +} +``` \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.3.0/user/examples/gtfs-static.md.license b/apps/docs/versioned_docs/version-0.3.0/user/examples/gtfs-static.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/examples/gtfs-static.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.3.0/user/examples/workbooks-xlsx.md b/apps/docs/versioned_docs/version-0.3.0/user/examples/workbooks-xlsx.md new file mode 100644 index 00000000..a46e886a --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/examples/workbooks-xlsx.md @@ -0,0 +1,97 @@ +--- +title: workbooks-xlsx +--- + +```jayvee +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// Example 1: LightTrapping +// Learning goals: +// - Understand how to work with XLSX files and workbooks + +// 1. This Jayvee model describes a pipeline +// from a XLSX file with multiple Sheets in the web +// to a SQLite file sink. +pipeline LightTrappingSiliconSolarCellsPipeline { + // 2. We directly get the xlsx file from the web via the HttpExtractor + // The data is provided under CC BY-SA 4.0 + // Saive, Rebecca (2023). Data supporting the publication: + // Light trapping in thin silicon solar cells: a review on fundamentals and technologies. + // 4TU.ResearchData. Dataset. https://doi.org/10.4121/14554815.v1 + block LightTrappingSiliconSolarCellsExtractor oftype HttpExtractor { + url: "https://figshare.com/ndownloader/files/27923598"; + } + + // 3. The incoming file is interpreted as a XLSX file and transformed into a Workbook + // Workbooks contain at least 1 Sheet. Every sheet has a unique name. + block LightTrappingSiliconSolarCellsTextXLSXInterpreter oftype XLSXInterpreter { + + } + + // 4.1 Here, we pick one sheet with the name 'RefractiveIndexSi GaAs' from the Workbook to use within our pipeline. + // The output type from SheetPicker is Sheet, which was already introduced in the cars example + block LightTrappingSiliconSolarCellsSheetpicker oftype SheetPicker { + sheetName: 'RefractiveIndexSi GaAs'; + } + + block NameHeaderWriter oftype CellWriter { + at: range F1:L1; + write: ["F","G","nm","wl","n2", "k2", "alpha (cm-1)2"]; + } + + block LightTrappingSiliconSolarCellsTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "Wavelength" oftype integer, + "Wavelength (µm)" oftype decimal, + "n" oftype decimal, + "k" oftype text, + "alpha (cm-1)" oftype text, + "nm" oftype decimal, + "n2" oftype text, + "k2" oftype decimal, + "alpha (cm-1)2" oftype decimal + ]; + } + + block LightTrappingSiliconSolarCellsLoader oftype SQLiteLoader { + table: "LightTrappingSiliconSolarCells"; + file: "./LightTrappingSiliconSolarCells.sqlite"; + } + + // 4.2 Here, we pick another sheet named 'Wavelength thickness trapping' from the Workbook + block SecondLightTrappingSiliconSolarCellsSheetpicker oftype SheetPicker { + sheetName: 'Wavelength thickness trapping'; + } + + block SecondLightTrappingSiliconSolarCellsTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "n" oftype decimal, + "Wavelength (µm)" oftype decimal, + ]; + } + + block SecondLightTrappingSiliconSolarCellsLoader oftype SQLiteLoader { + + table: "SecondLightTrappingSiliconSolarCells"; + file: "./LightTrappingSiliconSolarCells.sqlite"; + } + + LightTrappingSiliconSolarCellsExtractor + -> LightTrappingSiliconSolarCellsTextXLSXInterpreter + -> LightTrappingSiliconSolarCellsSheetpicker + -> NameHeaderWriter + -> LightTrappingSiliconSolarCellsTableInterpreter + -> LightTrappingSiliconSolarCellsLoader; + + // 5. Once the XLSX file is interpreted, we can split the pipeline and + // work separately on the different sheets from our input file + LightTrappingSiliconSolarCellsTextXLSXInterpreter + -> SecondLightTrappingSiliconSolarCellsSheetpicker + -> SecondLightTrappingSiliconSolarCellsTableInterpreter + -> SecondLightTrappingSiliconSolarCellsLoader; +} +``` \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.3.0/user/examples/workbooks-xlsx.md.license b/apps/docs/versioned_docs/version-0.3.0/user/examples/workbooks-xlsx.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/examples/workbooks-xlsx.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.3.0/user/expressions.md b/apps/docs/versioned_docs/version-0.3.0/user/expressions.md new file mode 100644 index 00000000..99e612a5 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/expressions.md @@ -0,0 +1,80 @@ +--- +sidebar_position: 7 +--- + +# Expressions + +Expressions in Jayvee are arbitrarily nested statements. They consist of: +- literals (e.g., numbers `5` or strings `"Example"`) +- variables (e.g., declared by `from` properties in [Transforms](./transforms.md)) +- operators (e.g., `*` or `sqrt`) + +Expressions get evaluated at runtime by the interpreter to a [Built-in ValueType](./valuetypes/builtin-valuetypes). + +### Example + +The following expression is evaluated to the `integer` `10`: `(2 + 3) * 2` + +The following expression is evaluated to the `integer` `3`: `floor (3.14)` + +The following expression is evaluated to the `boolean` `true`: `"Example" == "Example"` + +### List of Operators + +#### Arithmetics (binary operators) +- `+` for addition, e.g., `5 + 3` evaluates to `8` +- `-` for subtraction, e.g., `5 - 3` evaluates to `2` +- `*` for multiplication, e.g., `5 * 3` evaluates to `15` +- `/` for division, e.g., `6 / 3` evaluates to `2` +- `%` for modulo, e.g., `5 % 3` evaluates to `2` +- `pow` for power, e.g., `2 pow 3` evaluates to `8` +- `root` for root, e.g., `27 root 3` evaluates to `3` + +#### Arithmetics (unary operators) +- `+` for positive signing, e.g., `+5` evaluates to `5` +- `-` for negative signing, e.g., `-5` evaluates to `-5` +- `sqrt` for square root, e.g., `sqrt 9` evaluates to `3` +- `foor` for flooring a number, e.g., `floor 5.3` evaluates to `5` +- `ceil` for ceiling a number, e.g., `floor 5.3` evaluates to `6` +- `round` for rounding a number, e.g., `floor 5.3` evaluates to `5` + +#### Relational (binary operators) +- `<` for smaller, e.g., `3 < 3` evaluates to `false` +- `<=` for smaller or equal, e.g., `3 <= 3` evaluates to `true` +- `>` for greater, e.g., `3 > 3` evaluates to `false` +- `>=` for greater or equal, e.g., `3 >= 3` evaluates to `true` +- `==` for equal, e.g., `3 == 3` evaluates to `true` +- `!=` for not equal, e.g., `3 != 3` evaluates to `false` + +#### Logical (binary operators) +- `and` for a logical and (both need to be true to evaluate to true) +- `or` for a logical or (at least left or right needs to be true to evaluate to true) +- `xor` for a logical xor (either left or right needs to be true to evaluate to true) + +#### Logical (unary operators) +- `not` for logical negation, `not true` evaluates to `false` + +#### Others (binary operators) +- `matches` for a regex match, e.g., `"A07" matches /^[A-Z0-9]*$/` evaluates to `true` +- `in` for inclusion in an array, e.g., `"a" in ["a", "b", "c"]` evaluates to `true` + +### Operator Details + +#### `in` Operator + +The `in` operator checks whether a value is included in a collection of values. For example: + +```jayvee +4.5 in [3, 6.5] // evaluates to false +3 in [3.0, 6.5] // evaluates to true +"a" in ["a", "b", "c"] // evaluates to true +``` + +The operator supports `text`, `integer` and `decimal` values as operands. The compatibility of left and right operand types follows these rules: +- For the `in` operator we have a type for the needle (left operand) and a type for the elements in the haystack (right operand). +- There is an automated type conversion as long as it is lossless and clearly defined (integer to decimal as of now). +- We allow any combination of operands that has either: (i) An automated type conversion from needle type (left operand) to the type of the elements in the haystack (right operand), or (ii) the other way around. + + +### Further reading +For a deeper documentation of how expressions and operators work internally, refer to the [developer docs](../dev/04-guides/04-expressions-and-operators.md). \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.3.0/user/expressions.md.license b/apps/docs/versioned_docs/version-0.3.0/user/expressions.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/expressions.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.3.0/user/intro.md b/apps/docs/versioned_docs/version-0.3.0/user/intro.md new file mode 100644 index 00000000..33e4bb6a --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/intro.md @@ -0,0 +1,99 @@ +--- +sidebar_position: 1 +--- + +# Introduction to Jayvee + +Jayvee is a domain-specific language (DSL) for automated processing of data pipelines. +The Jayvee interpreter allows executing such data pipelines on local machines. +Data engineers can use Jayvee and its interpreter to clean and preprocess data for later activities like data science or machine learning. + +## Installation + +Install the interpreter via `npm`. You will need a **nodejs version >= 17.0.0**. + +```bash +npm install -g @jvalue/jayvee-interpreter +``` + +You can install a specific version using the `@`-syntax, e.g., version `0.0.17`: + +```bash +npm install -g @jvalue/jayvee-interpreter@0.0.17 +``` + +## Update + +Updating the interpreter is done by reinstalling it using `npm`. Make sure to also update the [VSCode plugin](#vscode-plugin) to match the installed interpreter if you use it. + +```bash +npm install -g @jvalue/jayvee-interpreter +``` + +## Usage + +### Show help + +```console +jv -h +``` + +### Run a `.jv` file + +```console +jv <file> +``` + +Run with **additional debug output**: + +```console +jv <file> -d +``` + +With **runtime parameters**: + +```console +jv -e <param>=<value> -e <param>=<value> ... <file> +``` + +### Debug a `.jv` file + +Print debugging is further configured by the parameters `--debug-granularity` and `--debug-target`. + +```console +jv <file> -d -dg peek +``` + +The value of the parameter `--debug-granularity` (short `-dg`) can have the following values: + +- `peek` to log a short summary, including a small subset of data +- `exhaustive` to log a summary, including the full data +- `minimal` to log a summary, including no additional data (default). + To see logs, debugging has to be enabled using the `-d` flag. + +```console +jv <file> -d --debug-granularity peek +``` + +The parameter `--debug-target` (short `-dt`) allows to specify which blocks should be logged for debugging. Separate block names by comma if multiple blocks are targeted. All blocks are logged if the parameter is omitted. + +```console +jv <file> -d --debug-granularity peek --debug-target MyExtractorBlock,MySinkBlock +``` + +## Examples + +You can find multiple examples [here](https://github.com/jvalue/jayvee/tree/main/example). Copy them to your local file system and execute them with the `jv` command on your command line (see [usage](#usage)). + +## VSCode Plugin + +To set up Jayvee locally in VS Code, you need to install the latest Jayvee VS Code extension. +To install the most recent extension, go to our [latest release](https://github.com/jvalue/jayvee/releases/latest) +and download the `jayvee.vsix` file from the release assets. +Next, go to [this page](https://code.visualstudio.com/docs/editor/extension-marketplace#_install-from-a-vsix) and +follow the instructions for installing the downloaded extension. + +## Troubleshooting + +1. Error `structuredClone is not defined` + - Please make sure you use node version 17+. diff --git a/apps/docs/versioned_docs/version-0.3.0/user/intro.md.license b/apps/docs/versioned_docs/version-0.3.0/user/intro.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/intro.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.3.0/user/runtime-parameters.md b/apps/docs/versioned_docs/version-0.3.0/user/runtime-parameters.md new file mode 100644 index 00000000..bbdab1ed --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/runtime-parameters.md @@ -0,0 +1,27 @@ +--- +sidebar_position: 9 +--- + +# Runtime Parameters + +Property values in Jayvee can be assigned to `values` or left open for later configuration via `runtime parameters`. + +## Syntax + +Runtime parameters are indicated by the `requires` keyword, followed by the identifier of the parameter. Example + +```jayvee +block CarsLoader oftype SQLiteLoader { + table: "Cars"; + file: requires CARS_SQLITE_FILE; +} +``` + +## CLI + +The interpreter CLI has to define all existing runtime parameters for execution. +Use the CLI flag `-e` to to define them as key-value pairs of identifier and value. + +```console +jv -e <param>=<value> -e <param>=<value> ... <file> +``` diff --git a/apps/docs/versioned_docs/version-0.3.0/user/runtime-parameters.md.license b/apps/docs/versioned_docs/version-0.3.0/user/runtime-parameters.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/runtime-parameters.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.3.0/user/transforms.md b/apps/docs/versioned_docs/version-0.3.0/user/transforms.md new file mode 100644 index 00000000..bf3dfd7d --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/transforms.md @@ -0,0 +1,77 @@ +--- +sidebar_position: 8 +--- + +# Transforms + +Transforms are a concept in Jayvee to define the transformation of individual values. +They are similar to functions in programming languages, i.e. they perform computations on some input values and produce output values. Transform work by mapping input values to outputs using [expressions](./expressions.md). + +:::info Important + +Up to version `0.0.16`, we only supported a single input for transformers! + +::: + +:::info Important + +In its current state, Jayvee only supports a arbitrary numbers of inputs and a single output for transforms. +For the future, it is planned to support arbitrary numbers for outputs as well. + +::: + + +## Syntax + +The general syntax of transforms looks like this: + +```jayvee +transform <name> { + from <inputName> oftype <inputValuetype>; + to <outputName> oftype <outputValuetype>; + + <outputName>: <expression>; +} +``` + +The `transform` keyword is used to define a transform and give it a name. +The curly braces denote the body of the transform. + +The body first contains the definitions of input and output ports. +Input ports are defined using the `from` keyword whereas output ports use the `to` keyword. +Next, they are given a name and, after the `oftype` keyword, typed with a valuetype. + +Below, there needs to be an output assignment for each output port. +The output assignment defines how a particular output value is computed. +They consist of the name of an output port, followed by a `:`. +Next, an [expression](./expressions.md) specifies how the output value shall be computed. +Names of input ports can be used in such an expression to refer to input values. + +### Example + +The following transform converts temperature values from degree Celsius to Kelvin: + +```jayvee +transform CelsiusToKelvin { + from tempCelsius oftype decimal; + to tempKelvin oftype decimal; + + tempKelvin: tempCelsius + 273.15; +} +``` + +The following transform converts a text based status into a boolean value, `true` if the text is `Active`, `false` for any other value: + +```jayvee +transform StatusToBoolean { + from statusText oftype text; + to statusBoolean oftype boolean; + + statusBoolean: statusText == "Active"; +} +``` + +## Applying transforms to table columns + +Transforms can be applied to columns of a table. +Please refer to the documentation of the [`TableTransformer` block type](./block-types/TableTransformer.md) to find out how. diff --git a/apps/docs/versioned_docs/version-0.3.0/user/transforms.md.license b/apps/docs/versioned_docs/version-0.3.0/user/transforms.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/transforms.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.3.0/user/valuetypes/_category_.json b/apps/docs/versioned_docs/version-0.3.0/user/valuetypes/_category_.json new file mode 100644 index 00000000..ed1ce95f --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/valuetypes/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Valuetypes", + "position": 5, + "link": { + "type": "generated-index", + "description": "Jayvee supports these different kinds of valuetypes." + } +} diff --git a/apps/docs/versioned_docs/version-0.3.0/user/valuetypes/_category_.json.license b/apps/docs/versioned_docs/version-0.3.0/user/valuetypes/_category_.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/valuetypes/_category_.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.3.0/user/valuetypes/builtin-valuetypes.md b/apps/docs/versioned_docs/version-0.3.0/user/valuetypes/builtin-valuetypes.md new file mode 100644 index 00000000..0bc36dcb --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/valuetypes/builtin-valuetypes.md @@ -0,0 +1,98 @@ +--- +title: Built-in Valuetypes +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +# Description + +For an introduction to valuetypes, see the [Core Concepts](../core-concepts). +Built-in valuetypes come with the basic version of Jayvee. +They are the basis for more restricted [Primitive Valuetypes](./primitive-valuetypes) +that fullfil [Constraints](./primitive-valuetypes#constraints). + +# Available built-in valuetypes + +## Boolean + +### Description + +A boolean value. +Examples: true, false + +### Example 1 + +```jayvee +block ExampleTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "columnName" oftype boolean + ]; +} +``` + +A block of type `TableInterpreter` that + interprets data in the column `columnName` as `boolean`. + +## Decimal + +### Description + +A decimal value. +Example: 3.14 + +### Example 1 + +```jayvee +block ExampleTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "columnName" oftype decimal + ]; +} +``` + +A block of type `TableInterpreter` that + interprets data in the column `columnName` as `decimal`. + +## Integer + +### Description + +An integer value. +Example: 3 + +### Example 1 + +```jayvee +block ExampleTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "columnName" oftype integer + ]; +} +``` + +A block of type `TableInterpreter` that + interprets data in the column `columnName` as `integer`. + +## Text + +### Description + +A text value. +Example: "Hello World" + +### Example 1 + +```jayvee +block ExampleTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "columnName" oftype text + ]; +} +``` + +A block of type `TableInterpreter` that + interprets data in the column `columnName` as `text`. diff --git a/apps/docs/versioned_docs/version-0.3.0/user/valuetypes/builtin-valuetypes.md.license b/apps/docs/versioned_docs/version-0.3.0/user/valuetypes/builtin-valuetypes.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/valuetypes/builtin-valuetypes.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.3.0/user/valuetypes/primitive-valuetypes.md b/apps/docs/versioned_docs/version-0.3.0/user/valuetypes/primitive-valuetypes.md new file mode 100644 index 00000000..92400a53 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/valuetypes/primitive-valuetypes.md @@ -0,0 +1,48 @@ +--- +sidebar_position: 2 +--- +# Primitive ValueTypes + +`Primitive ValueTypes` are based on `Built-in ValueTypes` and use a collection of constraints to restrict the range of valid values. +Such constraints are implicitly connected via a logical `AND` relation. +Note that the `Constraints` need to be applicable to the base-type of the `ValueType` - indicated by the identifier after the keyword `oftype`: + +```jayvee +valuetype GasFillLevel oftype integer { + constraints: [ GasFillLevelRange ]; +} +``` + + +## Constraints + +`Constraints` for `ValueTypes` declare the validity criteria that each concrete value is checked against. + +### Syntax 1: Expression syntax + +The syntax of expression-based `Constraints` uses an expression that evaluates to `true` or `false` for the given `value`. The type of the values the expression is working in is indicated ofter the keyword `on`: + +```jayvee +constraint GasFillLevelRange on decimal: + value >= 0 and value <= 100; +``` + +Refer to the [Expression documentation](../expressions.md) for further reading on expressions. + + +### Syntax 2: Block-like syntax + +The syntax of `Constraints` is similar to the syntax of `Blocks`. +The availability of property keys and their respective `ValueTypes` is determined by the type of the `Constraint` - indicated by the identifier after the keyword `oftype`: + +```jayvee +constraint GasFillLevelRange oftype RangeConstraint { + lowerBound: 0; + lowerBoundInclusive: true; + upperBound: 100; + upperBoundInclusive: true; +} +``` + +Note that the type of `Constraint` also determines its applicability to `ValueTypes`. +For instance, a `RangeConstraint` can only be applied to the numerical types `integer` and `decimal`. \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.3.0/user/valuetypes/primitive-valuetypes.md.license b/apps/docs/versioned_docs/version-0.3.0/user/valuetypes/primitive-valuetypes.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.3.0/user/valuetypes/primitive-valuetypes.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.4.0/dev/01-intro.md b/apps/docs/versioned_docs/version-0.4.0/dev/01-intro.md new file mode 100644 index 00000000..fef3c08e --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/dev/01-intro.md @@ -0,0 +1,52 @@ +--- +sidebar_position: 1 +--- + +# Introduction for Jayvee Developers + +## How to contribute + +In order to contribute to the Jayvee project, please have a look at our [contribution guide](https://github.com/jvalue/jayvee/blob/main/CONTRIBUTING.md). Before planning a contribution, please read the [design principles](./05-design-principles.md) and consider if your changes fit the vision expressed there. + +The overall project is licensed under the `AGPL-3.0-only` license and is compliant to the [REUSE Specification](https://reuse.software/spec/) by the [Free Software Foundation Europe](https://fsfe.org/). +Because of this, contributions are required to adhere to the license and follow that specification. +More details on this topic can be found [here](./02-dev-processes/03-licensing-and-copyright.md). + +And last but not least, please read and follow our [code of conduct](https://github.com/jvalue/jayvee/blob/main/CODE_OF_CONDUCT.md). + +## Project overview + +The Jayvee repository is located [here](https://github.com/jvalue/jayvee) on GitHub. +It uses an [Nx mono-repository](https://nx.dev/) setup and contains all related projects, most notably the Jayvee language server and interpreter. +Please have a look at the [architecture overview](./03-architecture-overview.md) for more details. + +## Prerequisites + +Node.js and npm are required for development. +It is recommended to use their LTS version to avoid any potential compatibility issues. +Also, refer to this [quick start guide](https://github.com/jvalue/jayvee#development-quickstart) for developers. + +In order to run the Jayvee VS Code extension during development, VS Code is required as IDE. +Using VS Code also allows installing the [Langium VS Code extension](https://marketplace.visualstudio.com/items?itemName=langium.langium-vscode) for better IDE support when working with Langium grammar files. + +## Resources for getting started + +### Langium + +- [**Langium documentation**](https://langium.org/docs/) +- Practical examples using Langium: + - [Langium examples](https://github.com/langium/langium/tree/main/examples): Official example languages by Langium + - [Langium's grammar language](https://github.com/langium/langium/tree/main/packages/langium): The implementation of Langium's own grammar language + - [Language server for SQL](https://github.com/langium/langium-sql): An implementation of a language server for SQL + - [MiniLogo DSL](https://github.com/langium/langium-minilogo): A domain-specific language for drawing pictures + - [Lox language](https://github.com/langium/langium-lox): An implementation of the Lox scripting language (also see the "Crafting Interpreters" book below) + - [SimpleUI DSL](https://github.com/TypeFox/langium-ui-framework): A domain-specific language for generating user interfaces + - [STPA-DSL](https://github.com/kieler/stpa): A domain-specific language for System-Theoretic Process Analysis +- [Langium playground](https://langium.org/playground/): A tool for testing Langium grammars and visualizing resulting ASTs +- [Typefox blog](https://www.typefox.io/blog/): A blog by the company behind Langium, mostly posting Langium-related content. + +### Building compilers / interpreters / DSLs + +- [Crafting Interpreters](https://craftinginterpreters.com/contents.html): A book by Robert Nystrom on implementing an interpreter for the Lox scripting language +- [DSL Engineering](https://voelter.de/dslbook/markusvoelter-dslengineering-1.0.pdf): A book by Markus Voelter on designing, implementing and using domain-specific languages +- [Awesome Compilers](https://github.com/aalhour/awesome-compilers#readme): A list featuring many resources on compilers and interpreters diff --git a/apps/docs/versioned_docs/version-0.4.0/dev/01-intro.md.license b/apps/docs/versioned_docs/version-0.4.0/dev/01-intro.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/dev/01-intro.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.4.0/dev/02-dev-processes/01-rfc-process.md b/apps/docs/versioned_docs/version-0.4.0/dev/02-dev-processes/01-rfc-process.md new file mode 100644 index 00000000..07444085 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/dev/02-dev-processes/01-rfc-process.md @@ -0,0 +1,13 @@ +--- +title: Language Design Process (RFCs) +sidebar_position: 1 +--- + +We use RFCs to discuss changes to the language before implementing them. You can have a look at all closed (accepted / rejected) RFCs [here](https://github.com/jvalue/jayvee/pulls?q=is%3Apr+is%3Aclosed+RFC+), and all RFCs under discussion [here](https://github.com/jvalue/jayvee/pulls?q=is%3Apr+is%3Aopen+RFC). + +If you want to contribute an RFC please follow these steps: +1. Make a copy of the **template** at `rfc/0000-rfc-template.md` and place it into the `rfc` folder. +2. Create a draft for the RFC on a new branch. Follow the `TODOs` in template to do so. +3. Open a pull request with prefix `RFC <number>` in the title. +4. Address the reviews. Consider opening a new PR if major things need to be addressed and the discussion log becomes too confusing. +5. Once accepted, create an issue with the necessary steps to implement the RFC. \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.4.0/dev/02-dev-processes/01-rfc-process.md.license b/apps/docs/versioned_docs/version-0.4.0/dev/02-dev-processes/01-rfc-process.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/dev/02-dev-processes/01-rfc-process.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.4.0/dev/02-dev-processes/02-debug-vs-code-extension.md b/apps/docs/versioned_docs/version-0.4.0/dev/02-dev-processes/02-debug-vs-code-extension.md new file mode 100644 index 00000000..5fbcf07c --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/dev/02-dev-processes/02-debug-vs-code-extension.md @@ -0,0 +1,25 @@ +--- +title: Debugging via the VS Code extension +sidebar_position: 2 +--- + +The VS Code extension of Jayvee can be used to interactively debug the language server. +During development, when using VS Code as IDE, another instance of VS Code can be opened which has the most recent, locally built VS Code extension loaded. +This can be achieved using the launch configuration `Run extension` the `Run and Debug` side-menu of VS Code or by pressing the `F5` key if that launch configuration is already selected there. + +Note that there is no file watching mechanism involved. +So in order to reflect changes to the source code, the additional VS Code instance has to be **closed and reopened** as described above. + +## How to attach a debugger + +1. Use the launch configuration `Run extension` to open the additional VS Code instance with the extension loaded +2. Use the launch configuration `Attach to Language Server` to attach the debugger + +Any set breakpoints should now be marked as active and pause the execution once they are reached. + +## How to view logs of the language server + +In the additional VS Code instance, it is possible to view the logs of the language server. +They might also be helpful for debugging since any uncaught errors are logged with their stack trace by the Langium framework. + +To view the logs, open the bottom panel called `Output` and select `Jayvee` in the dropdown menu. diff --git a/apps/docs/versioned_docs/version-0.4.0/dev/02-dev-processes/02-debug-vs-code-extension.md.license b/apps/docs/versioned_docs/version-0.4.0/dev/02-dev-processes/02-debug-vs-code-extension.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/dev/02-dev-processes/02-debug-vs-code-extension.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.4.0/dev/02-dev-processes/03-licensing-and-copyright.md b/apps/docs/versioned_docs/version-0.4.0/dev/02-dev-processes/03-licensing-and-copyright.md new file mode 100644 index 00000000..201e21ca --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/dev/02-dev-processes/03-licensing-and-copyright.md @@ -0,0 +1,69 @@ +--- +title: Licensing and copyright +sidebar_position: 3 +--- + +The [Jayvee repository](https://github.com/jvalue/jayvee) is REUSE compliant, meaning that it fulfills the +[REUSE Specification](https://reuse.software/spec/) by the [Free Software Foundation Europe](https://fsfe.org/) (FSFE). +This makes clear under which license each project file is licensed under and who owns the copyright, not only for +humans but also for machines. + +This is done by explicitly adding copyright and licensing information to each file of the project. This is achieved +by either using a comment header or a separate `*.license` file in case comments are not possible. + +See <https://reuse.software/> more information. + +## What license is used in this project and who is the copyright holder? + +The entire project is licensed under [AGPL-3.0-only](https://spdx.org/licenses/AGPL-3.0-only.html), the +license file can be found [here](https://github.com/jvalue/jayvee/blob/main/LICENSES/AGPL-3.0-only.txt). +The copyright holder is the [Friedrich-Alexander-Universität Erlangen-Nürnberg](https://www.fau.eu/). + +## How to submit a REUSE compliant contribution? + +In case you want to contribute to the project, you will need to ensure that all of your contributed files are REUSE +compliant. In order to achieve this, you need to add a key-value pair for both copyright and licensing information +following this schema: + +``` +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only +``` + +In case a file allows comments, use single-line comments to add the copyright and licensing information at the top +of the file. Otherwise, create a corresponding `*.license` file with the text above as its content. You can have a +look at some existing project files to get an impression on it is done in practice. + +For files with common file extensions, you can use the [reuse CLI tool](https://github.com/fsfe/reuse-tool) to add +licensing and copyright information automatically. + +For more details, you can have a look at the [Getting Started tutorial](https://reuse.software/tutorial/) on the REUSE +website. + +## How to validate REUSE compliance? + +When you make a contribution and open a new pull request, the CI checks whether your contribution is REUSE compliant +using the [reuse CLI tool](https://github.com/fsfe/reuse-tool). + +In order to validate REUSE compliance in your local development environment, you have to install the +[reuse CLI tool](https://github.com/fsfe/reuse-tool) and run the following command in the projects' root folder: + +```bash +reuse lint +``` + +You can also set up a pre-commit hook, so the above command is run before each commit. +See [here](https://reuse.readthedocs.io/en/latest/readme.html#run-as-pre-commit-hook) for details on how to set it up. + +## How to hide `*.license` files in IDEs + +During development, the file explorer of your IDE may be cluttered due to the numerous `*.license` files in the +project. Luckily, most IDEs allow hiding certain files, e.g. by specifying a pattern to exclude them from the +explorer. + +Below, you can find instructions on how to hide `*.license` files in commonly used IDEs: +- **Visual Studio Code**: Add `**/*.license` to the +[`files.exclude` setting](https://code.visualstudio.com/docs/getstarted/userinterface#_explorer) +- **WebStorm**: Add `*.license` to the +[excluded files setting](https://www.jetbrains.com/help/webstorm/configuring-project-structure.html#exclude-by-pattern) diff --git a/apps/docs/versioned_docs/version-0.4.0/dev/02-dev-processes/03-licensing-and-copyright.md.license b/apps/docs/versioned_docs/version-0.4.0/dev/02-dev-processes/03-licensing-and-copyright.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/dev/02-dev-processes/03-licensing-and-copyright.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.4.0/dev/02-dev-processes/04-release-jayvee-version.md b/apps/docs/versioned_docs/version-0.4.0/dev/02-dev-processes/04-release-jayvee-version.md new file mode 100644 index 00000000..374f3468 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/dev/02-dev-processes/04-release-jayvee-version.md @@ -0,0 +1,22 @@ +--- +title: Releasing a new Jayvee version +sidebar_position: 4 +--- + +## Version Numbers + +In this early stage of the project we do not yet follow [semantic versioning](https://semver.org/) since we expect the introduction of breaking changes frequently. +To indicate that, we only release alpha versions where the `version` is incremented with every release. +- For the npm packages, we use the version `0.0.<version>`. +- For the GitHub releases, we use the git tag `v0.0.<version>-alpha`. + +## Jayvee Release Procedure + +For releasing a new version of Jayvee, you need to complete the following steps: + +1. Increment the version in the `package.json` file. +2. Run `npm i` to update the `package-lock.json`. +3. Run `npx nx run docs:version-snapshot` to generate a snapshot of the docs for the previous version. +4. If you are on a feature or dev branch, merge into main. +5. Create a GitHub release on the main branch. Attach a changelog. +6. The CI/CD will deal with the rest. diff --git a/apps/docs/versioned_docs/version-0.4.0/dev/02-dev-processes/04-release-jayvee-version.md.license b/apps/docs/versioned_docs/version-0.4.0/dev/02-dev-processes/04-release-jayvee-version.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/dev/02-dev-processes/04-release-jayvee-version.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.4.0/dev/02-dev-processes/_category_.json b/apps/docs/versioned_docs/version-0.4.0/dev/02-dev-processes/_category_.json new file mode 100644 index 00000000..c8f51245 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/dev/02-dev-processes/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Development Process", + "position": 2, + "link": { + "type": "generated-index", + "description": "Here you can find general processes around developing Jayvee." + } +} diff --git a/apps/docs/versioned_docs/version-0.4.0/dev/02-dev-processes/_category_.json.license b/apps/docs/versioned_docs/version-0.4.0/dev/02-dev-processes/_category_.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/dev/02-dev-processes/_category_.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.4.0/dev/03-architecture-overview.md b/apps/docs/versioned_docs/version-0.4.0/dev/03-architecture-overview.md new file mode 100644 index 00000000..b609a613 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/dev/03-architecture-overview.md @@ -0,0 +1,51 @@ +--- +title: Architecture overview +sidebar_position: 4 +--- + +Jayvee has a clear separation between the language itself and its execution. +This guide gives an overview of the overall architecture. + +## Language Server + +On the pure language side, the central project is the [language server](https://github.com/jvalue/jayvee/tree/main/libs/language-server) which is heavily built upon the [Langium framework](https://langium.org/). +It contains the syntax definition (i.e. the grammar) and is capable of performing static semantic analysis on models, so invalid models can be rejected and errors are reported to the user. +It uses the [Language Server Protocol](https://microsoft.github.io/language-server-protocol/) (LSP) for communicating with IDEs in order to provide common features such as diagnostics, auto completion and much more. + +**Note:** The [Langium framework](https://langium.org/) generate TypeScript files for the abstract syntax tree (AST), based on the grammar specification. +The following locations might be especially helpful to understand the grammar and its AST: + +- The Langium grammar files (see [here](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/grammar) or locally at `libs/language-server/src/grammar`; with `.langium` file ending). These files define the **syntax of the language**. +- The generated TypeScript AST files (execute `npm run generate` to generate them at `libs/language-server/src/lib/ast/generated` in your local repository). These files include **TypeScript interfaces for AST nodes** (e.g., `BlockDefinition`) and **guard methods** (e.g., `isBlockDefinition`). + They reflect the input of the grammar files regarding naming. +- The remaining source files of the language server implement the language server protocol (LSP) and the additional validations beyond the syntax of Jayvee. + +## Interpreter + +The Jayvee [interpreter](https://github.com/jvalue/jayvee/tree/main/apps/interpreter) on the other hand is capable of running Jayvee models. +Therefore, it uses the language server as a library to perform lexing, parsing and semantic analysis on given models. +In case no errors were found during these phases, it executes the model by interpreting it. +This means that models are not compiled to any other language, like a compiler does, but rather directly executed on the fly without any code generation involved. +The interpreter comes with a command-line interface (CLI) for users, so they are able to execute Jayvee models easily. + +The following diagram visualizes the architecture described so far: + +```mermaid +graph LR +model[Jayvee Model] --> lexical(Lexical Analysis) +subgraph Jayvee Interpreter + subgraph Jayvee Language Server + subgraph Langium Framework + lexical --> syntax(Syntax Analysis) + end + syntax --> semantic(Semantic Analysis) + end + semantic --> interpretation(Interpretation) +end +``` + +## Jayvee Extensions + +Lastly, there are Jayvee extensions for adding additional features to Jayvee. +See [here](./04-guides/06-jayvee-extensions.md) for more details. +It is worth pointing out that extensions also follow the separation between language and execution described above. diff --git a/apps/docs/versioned_docs/version-0.4.0/dev/03-architecture-overview.md.license b/apps/docs/versioned_docs/version-0.4.0/dev/03-architecture-overview.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/dev/03-architecture-overview.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.4.0/dev/04-guides/01-jayvee-grammar.md b/apps/docs/versioned_docs/version-0.4.0/dev/04-guides/01-jayvee-grammar.md new file mode 100644 index 00000000..356fb09d --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/dev/04-guides/01-jayvee-grammar.md @@ -0,0 +1,34 @@ +--- +title: The Jayvee grammar +sidebar_position: 1 +--- + +The grammar of Jayvee describes the syntax and structure of the language. +It is located in the language server project. +The grammar itself is written in [Langium's own grammar language](https://langium.org/docs/grammar-language/) which is similar to [Xtext](https://www.eclipse.org/Xtext/) grammars. +Such grammar files are easily identifiable via their `.langium` file extension. + +The grammar is used to generate TypeScript interfaces that represent the Abstract Syntax Tree (AST) and different, semantically equivalent files to define syntax highlighting for different applications. +For instance, a [TextMate](https://macromates.com/manual/en/language_grammars) file is generated for the syntax highlighting in the Jayvee VS Code extension whereas a [Monarch](https://microsoft.github.io/monaco-editor/monarch.html) file is generated for the syntax highlighting in the Monaco editor. + +For further information on the grammar language of Langium, visit the corresponding [Langium documentation](https://langium.org/docs/grammar-language/). + +## Working with the grammar + +To run the code generation, either use `npm run generate` for solely the code generation or `npm run build` for an entire build. The code generation also generates further code, like the standard library. + +Whenever the grammar is changed and the code generation is run during development, it is advisory to **close and reopen the IDE**, so the changes are noticed and the file indexing is updated. + +## How to rename AST nodes + +Renaming AST nodes is not as straight forward as one might initially assume. +When renaming individual rules in the grammar, just the generated TypeScript code in `ast.ts` will reflect the renaming, but not the rest of the codebase. + +The following steps explain how to rename an AST node, so the change is reflected in the entire codebase. As an example, an AST node called `A` is supposed to be renamed to `B`: + +- Open the `ast.ts` file in the language server project +- Locate the `A` interface / type and the `isA` typeguard +- Use the rename feature by the IDE to perform the renaming from `A` to `B` and from `isA` to `isB` +- Open the grammar and locate the `A` rule +- Use the rename feature by the Langium VS Code extension to perform the renaming from `A` to `B` +- Run `npm run generate` diff --git a/apps/docs/versioned_docs/version-0.4.0/dev/04-guides/01-jayvee-grammar.md.license b/apps/docs/versioned_docs/version-0.4.0/dev/04-guides/01-jayvee-grammar.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/dev/04-guides/01-jayvee-grammar.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.4.0/dev/04-guides/02-working-with-the-ast.md b/apps/docs/versioned_docs/version-0.4.0/dev/04-guides/02-working-with-the-ast.md new file mode 100644 index 00000000..895cae12 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/dev/04-guides/02-working-with-the-ast.md @@ -0,0 +1,148 @@ +--- +title: Working with the AST +sidebar_position: 2 +--- + +The nodes of the Abstract Syntax Tree (AST) consist of types and interfaces generated from the language grammar. +See [here](./01-jayvee-grammar.md) for more information on that topic. + +The following sections provide practical guides and tips for working with nodes of the AST. + +## Dealing with potentially incomplete AST nodes + +According to the generated interfaces, properties of AST nodes are never `undefined`. +In practice however, this is not always the case. + +For example, consider the language server being confronted with an incomplete Jayvee model with syntax errors. +In such cases, properties of AST nodes are in fact `undefined`, despite their interface definition. +In the interpreter however, after lexical and syntactic analysis succeeded, it can be assumed that all AST nodes are complete (i.e. they have no undefined properties) and even that all references are resolved. + +In order to avoid accessing properties of AST nodes that are potentially undefined, it is recommended to access them via the [`?.` operator](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#optional-chaining) rather than the regular `.` operator. +Note that this might result in a conflict with the ESLint rule [`@typescript-eslint/no-unnecessary-condition`](https://typescript-eslint.io/rules/no-unnecessary-condition/), so it has to be disabled manually for such cases. + +For disabling the rule in an entire file, place this comment below the copyright and licensing header: + +```typescript +/* eslint-disable @typescript-eslint/no-unnecessary-condition */ +``` + +For just a single line, place this comment above that particular line: + +```typescript +// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition +``` + +<details> + +<summary>Full example</summary> + +Consider an exemplary AST node `A` with a property `x` of type `string`. To access that property safely: + +```typescript +import { A } from './ast' + +const astNode: A; + +// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition +const property: string | undefined = astNode?.x; +``` + +</details> + +## Usage of `assertUnreachable` + +Most times, it is beneficial to make case distinctions exhaustive, especially when working with AST nodes, properties with a [union literal type](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types) or enums. +Exhaustiveness in this context means, that the TypeScript compiler is supposed to yield an error if a case distinction does not cover all possibilities. + +Langium offers a function called `assertUnreachable` which is capable of enforcing exhaustiveness and producing compiler errors in case of violations. See the following examples to get an idea on how to use it in practice: + +<details> + +<summary>Example for an exhaustive switch statement on a union literal type</summary> + +```typescript +import { assertUnreachable } from 'langium'; + +const operator: '+' | '-'; + +switch(operator) { + case '+': { + // ... + break; + } + case '-': { + // ... + break; + } + default: { + // To ensure the switch being exhaustive on `operator`: + assertUnreachable(operator); + } +} +``` + +</details> + +<details> + +<summary>Example for an exhaustive if-elseif-else cascade using typeguards</summary> + +Consider the exemplary AST nodes `A`, `B` and `C` and that `A = B | C`: + +```typescript +import { assertUnreachable } from 'langium'; +import { A, B, isB, C, isC } from './ast' + +const astNode: A; + +if (isB(astNode)) { + // `astNode` has type `B` here +} else if (isC(astNode)) { + // `astNode` has type `C` here +} else { + // To ensure the if-elseif-else cascade being exhaustive on `astNode`: + assertUnreachable(astNode); +} +``` + +</details> + +## Usage of `assert` for expressing runtime expectations + +During development, it may occur that a certain condition is expected to **be always true** at runtime. +For example, an AST node being of a certain type or a property being defined. +The type system of TypeScript is not always able to infer such facts, so developers may have to express these expectations explicitly in their code. +Examples are [type assertions](https://www.typescriptlang.org/docs/handbook/advanced-types.html) or the [non-null assertion operator](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html#non-null-assertion-operator). +Their usage may be problematic in case the condition does not always hold, e.g. due to a bug in the program or a wrong expectation by the programmer. +In such cases, it is hard to locate the origin and debug the program because such operations are erased in the compiled JavaScript code. + +To avoid these issues, it is recommended to express such expectations as boolean expressions that are actually validated at runtime. +This can be easily achieved by using the `assert` function. +It evaluates a given boolean expression and throws an `AssertionError` in case it evaluates to `false`. +After calling `assert`, the type system of TypeScript assumes the condition to be `true` and afterwards narrows types accordingly. + +Here is an example of how to use it in practice: + +```typescript +// Import the `assert` function like this: +import { strict as assert } from 'assert'; + +import { A, B, isB } from './ast'; + +const astNode: A; +assert(isB(astNode)); +// Here `astNode` has type `B` + +const referenced = astNode?.reference?.ref; +assert(referenced !== undefined); +// Here `referred` is not `undefined` +``` + +## AST wrapper classes + +The generated interfaces for AST nodes in `ast.ts` are only meant to represent the AST structurally, they don't define any behavior. +Also, in case of syntactic sugar, there may be different kinds of AST nodes representing the same semantic language concept. + +To cope with this problem, there is the concept of an `AstNodeWrapper`. +An AST node wrapper is capable of wrapping AST nodes that represent the same semantic language concept and adding behavior to them via custom methods. +To get an impression on how this can be done in practice, please have a look at the [`PipeWrapper` class](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/lib/ast/wrappers/pipe-wrapper.ts) and the [`AstNodeWrapper` interface](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/lib/ast/wrappers/ast-node-wrapper.ts). diff --git a/apps/docs/versioned_docs/version-0.4.0/dev/04-guides/02-working-with-the-ast.md.license b/apps/docs/versioned_docs/version-0.4.0/dev/04-guides/02-working-with-the-ast.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/dev/04-guides/02-working-with-the-ast.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.4.0/dev/04-guides/03-validation-and-diagnostics.md b/apps/docs/versioned_docs/version-0.4.0/dev/04-guides/03-validation-and-diagnostics.md new file mode 100644 index 00000000..328d5728 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/dev/04-guides/03-validation-and-diagnostics.md @@ -0,0 +1,64 @@ +--- +title: Validation and diagnostics +sidebar_position: 3 +--- + +Validation in the context of Jayvee can be understood as semantic analysis of Jayvee models. +The purpose is to uncover errors in models besides lexing, parsing and linking errors that the Langium framework already detects out of the box. +In case such errors are found, the language server makes the IDE display a diagnostic. +This means that the location of the error is underlined and an error message is shown. + +For example, each pipeline is expected to contain at least one starting block that requires no input. +Therefore, the language server analyzes every pipeline in a Jayvee model and checks for the presence of such a starting block. +In case no such block is found, the IDE underlines the pipeline definition in a red color and displays an error message. + +## How to implement validation + +### For arbitrary AST nodes + +In the file [`validation-registry.ts`](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/lib/validation/validation-registry.ts) of the language server, there is a registry containing validation functions for various AST nodes. +A validation function provided for a certain kind of AST node is called for every occurrence of such a node in a Jayvee model. +E.g. when registering a validation function for `PipelineDefinition` nodes, that function gets called once for each pipeline. + +A validation function always operates on a single, concrete AST node. +Besides the AST node, a `ValidationContext` object is passed to the function for reporting diagnostics via its `accept` method. + +The overall validation for a specific kind of AST node is usually subdivided into smaller checks that are called sequentially. +This potentially allows aborting the validation early, i.e. before all checks were run. +Aborting early may be useful if errors were reported during previous checks. +This can be determined via the `ValidationContext` which keeps track whether any errors occurred so far. + +A practical example where this plays a role are blocks: +When the type of a block is unknown to the language server, it makes no sense to validate inputs / outputs and properties of that block. +So, in case an error was reported during the check of the block type, the validation of that block is discontinued at that point. + +### For properties of blocks and constraints + +Validating property values of blocks / constraints works a bit differently because their individual validation is already incorporated into the overall validation framework. + +In general, the validation logic for a property values is located in the meta information of the block type / constraint type. +There, properties are defined using the `PropertySpecification` interface. +In order to add validation logic to a certain property, a validation function needs to be added to that property. + +For more complex validations that need to take multiple property values into account, it is also possible to provide a more general validation function which operates on the entire `PropertyBody` AST node. + +See [`text-range-selector-meta-inf.ts`](https://github.com/jvalue/jayvee/blob/main/libs/extensions/std/lang/src/text-range-selector-meta-inf.ts) for an example which includes both cases described above. + +## Reporting diagnostics + +During validation, diagnostics can be reported by calling the `accept` method of the given `ValidationContext` object. + +The call requires the severity of the diagnostic (`error`, `warning`, `info` or `hint`), the error message and its location. +The location is described by a `DiagnosticInfo` object which contains the affected AST node and optionally some further restrictions. +For more details, have a look at the [`DiagnosticInfo` interface](https://github.com/langium/langium/blob/main/packages/langium/src/validation/validation-registry.ts) by Langium. + +## Common issues + +When working on validations, a diagnostic with the following message may unexpectedly occur: `An error occurred during validation: ...` + +Such a diagnostic is generated by the Langium framework if an error object is thrown within the validation. +In most cases, the reason is the access of an `undefined` AST node property or an `AssertionError`. +For more details on such errors and how to avoid them, have a look at the documentation on [how to work with the AST](./02-working-with-the-ast.md). + +One way locate the origin of such errors is to debug the Jayvee VS Code extension, see [here](../02-dev-processes/02-debug-vs-code-extension.md) for more details. +Another possibility is to debug the interpreter and set appropriate breakpoints in the language server codebase. diff --git a/apps/docs/versioned_docs/version-0.4.0/dev/04-guides/03-validation-and-diagnostics.md.license b/apps/docs/versioned_docs/version-0.4.0/dev/04-guides/03-validation-and-diagnostics.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/dev/04-guides/03-validation-and-diagnostics.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.4.0/dev/04-guides/04-expressions-and-operators.md b/apps/docs/versioned_docs/version-0.4.0/dev/04-guides/04-expressions-and-operators.md new file mode 100644 index 00000000..d201864e --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/dev/04-guides/04-expressions-and-operators.md @@ -0,0 +1,151 @@ +--- +title: Expressions and operators +sidebar_position: 4 +--- + +Jayvee supports arbitrarily nested expressions and offers a variety of different operators. +Such expressions are similar to those used in programming languages, and they are intended to be read and understood intuitively. + +The following sections explain the definition of expressions in the language grammar, their type inference mechanism, and how the evaluation of expressions works. +As a small practical example, you may also have a look at the [arithmetics example by Langium](https://github.com/langium/langium/tree/main/examples/arithmetics) which has a heavy focus on mathematical expressions and functions. + +## Expressions in the grammar + +Expressions in the Jayvee grammar are defined in [`expression.langium`](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/grammar/expression.langium) and consist of operators (unary / binary / ternary) and literals. +Unary operators only have a single operand (e.g. the `not` operator), binary operators require two operands (e.g. the `*` operator) and ternary operators require three operands. + +The grammar is written in a way that literals end up in the leaves of the resulting AST and the nodes above represent the operators. + +As an example, have a look at the following AST structure of the expression `(-3 + 5) * 7`: + +```mermaid +classDiagram + +class `:BinaryOperator` { + operator = '*' +} + +%% Uses spaces in the name so it is unique +class ` :BinaryOperator` { + operator = '+' +} + +class `:UnaryOperator` { + operator = '-' +} + +%% Uses spaces in the name so it is unique +class ` :NumericLiteral` { + value = 3 +} + +%% Uses spaces in the name so it is unique +class ` :NumericLiteral` { + value = 5 +} + +class `:NumericLiteral` { + value = 7 +} + +`:BinaryOperator` --> ` :BinaryOperator` : leftOperand +`:BinaryOperator` --> `:NumericLiteral` : rightOperand +` :BinaryOperator` --> `:UnaryOperator` : leftOperand +`:UnaryOperator` --> ` :NumericLiteral` : operand +` :BinaryOperator` --> ` :NumericLiteral` : rightOperand +``` + +The AST is constructed in a way that ensures an unambiguous evaluation order when using depth-first search. +This implies that the AST structure already reflects the precedence and associativity of the operators, and that parentheses do not need to be explicitly represented. + +For more details on how such a grammar for expressions can be realized, see the [official Langium documentation](https://langium.org/docs/grammar-language/#tree-rewriting-actions), [this blog post](https://www.typefox.io/blog/parsing-expressions-with-xtext) and the following two sections which discuss the precedence and associativity of operators. + +### Precedence of operators + +The grammar implicitly defines the precedence of operators. +The operator precedence defines the order in which operators are evaluated within an expression. +For example, multiplication has a higher precedence than addition, which leads to multiplications being performed before additions. +In order to manually override such precedence conventions, parentheses can be used in expressions. + +The diagram below shows a more extensive precedence hierarchy, focused on common arithmetic operators. +Note that the hierarchy is arranged in ascending order, according to the operator precedence. +Such an order is similar to how operators in the actual Jayvee grammar are arranged: + +```mermaid +graph TD + subgraph BinaryOperators + add/sub(Addition Operator / Subtraction operator) --> mul/div(Multiplication Operator / Division Operator) + mul/div --> exp/root(Exponential Operator / Root Operator) + end + subgraph UnaryOperators + exp/root --> plus/minus(Plus Operator / Minus Operator) + end + plus/minus --> literal(Numeric Literal) +``` + +In the Jayvee grammar, every level of precedence is represented by a single grammar rule. +Within each such rule, the grammar rule which defines the operators with the next higher precedence is referenced. +E.g. the `AdditiveExpression` rule refers to the `MultiplicativeExpression` rule because multiplicative operators are supposed to have the next higher precedence. + +In order to alter the precedence of operators, their hierarchy of grammar rules has to be adjusted accordingly in [`expression.langium`](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/grammar/expression.langium). + +### Associativity of operators + +The associativity of operators defines the order in which operators with the same precedence are evaluated when they appear in succession without parentheses. +Operators may be either **left-associative**, **right-associative** or **non-associative**. +For example, depending on the associativity of the binary `+` operator, the expression `a + b + c` has different semantics: + +- Left-associative: `(a + b) + c` (evaluation from left to right) +- Right-associative: `a + (b + c)` (evaluation from right to left) +- Non-associative: _syntax error_, the operator cannot be chained + +The associativity of operators is also defined in the Jayvee grammar, more specifically by the structure of the grammar rules that define operators. +For details on how to encode the different kinds of associativity in grammar rules, have a look at the "Associativity" section near the end of [this blog post](https://www.typefox.io/blog/parsing-expressions-with-xtext). +The patterns described in the linked article can be found in [`expression.langium`](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/grammar/expression.langium), so each operator grammar rule encodes its own associativity. + +## Typing of expressions + +Jayvee has a mechanism for inferring and validating the type of a given expression. +This is achieved using a recursive algorithm which performs depth-first search on a given expression: + +The base case for the algorithm are literals, i.e. the tree leaves of an expression. +Depending on the type of literal, their type can be inferred trivially (in case of value literals) or otherwise from the context where the expression is located. + +For operators, the types of their operands are first inferred via recursion, and then it is checked whether they are supported by the operator. +Next, given the operand types, the resulting type is computed. +Such behavior is defined in a _type computer class_ located [here](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/lib/ast/expressions/type-computers). +Additionally, in [`operator-registry.ts`](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/lib/ast/expressions/operator-registry.ts), a type computer is registered for each kind of operator. + +In case the algorithm fails to infer a type, e.g. due to unsupported operand types or unresolved references in literals, the resulting type is `undefined`. +In order to report diagnostics in such cases, a `ValidationContext` object can be supplied when calling the type inference. + +## Evaluation of expressions + +The evaluation has the purpose of computing a single value out of an expression. +For a successful evaluation, it is a **precondition** that **a type could successfully be inferred** from the respective expression. +Also, the **value for each free variable** in that expression (i.e. literals resembling placeholders for values) needs to be **known beforehand**. +Therefore, an `ExecutionContext` object is passed to the evaluation which holds values for free variables in the current context. + +### Algorithm + +The algorithm for evaluating expressions is also based on a recursive depth-first search, similar to how the type inference works: + +Again, literals are the base case. A value literal trivially evaluates to its own value. +Other literals that resemble free variables evaluate to their value provided by the given `ExecutionContext` object. + +Regarding operators, their operands first are evaluated recursively. +Next, using the concrete operand values, the operator computes its resulting value. +This is defined in operator evaluator classes located [here](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/lib/ast/expressions/evaluators). +The classes are registered in [operator-registry.ts](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/lib/ast/expressions/operator-registry.ts) for every operator, similar to the previously mentioned type computers. + +The result of an evaluation may be `undefined` in case any errors occurred. +If the preconditions were all met, such errors are most likely arithmetic errors (like division by zero). +It is possible to provide a `ValidationContext` object to the evaluation for reporting such errors as diagnostics. + +### Evaluation strategies + +There are different evaluation strategies to choose from. +They affect the way, expressions are evaluated and thus have an impact on their semantics: + +- `exhaustive`: A full depth-first search is performed, all parts of an expression are evaluated. +- `lazy`: An evaluation with the least effort is performed. Logical operators use [short circuit semantics](https://en.wikipedia.org/wiki/Short-circuit_evaluation) and binary operators don't evaluate their right operand if the left one evaluated to `undefined`. diff --git a/apps/docs/versioned_docs/version-0.4.0/dev/04-guides/04-expressions-and-operators.md.license b/apps/docs/versioned_docs/version-0.4.0/dev/04-guides/04-expressions-and-operators.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/dev/04-guides/04-expressions-and-operators.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.4.0/dev/04-guides/05-standard-library.md b/apps/docs/versioned_docs/version-0.4.0/dev/04-guides/05-standard-library.md new file mode 100644 index 00000000..6db17bd7 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/dev/04-guides/05-standard-library.md @@ -0,0 +1,48 @@ +--- +title: Working with the Standard Library +sidebar_position: 5 +--- + +Jayvee ships with its own standard library on board, including the most often used valuetypes, transformations, and so on. +The standard library itself is written in `.jv` files [here](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/stdlib/). + +## Standard Library Contents + +The following elements are part of the standard library: + +## Builtin Contents + +The implementations of builtin contents are not expressed in Jayvee itself but on the TypeScript layer. Examples: + +- **Builtin valuetypes**: These valuetypes are the base for defining user-defined valuetypes in Jayvee, e.g., `text`, `integer`, `decimal`, `boolean`. +- **Builtin iotypes**: These iotypes are used to describe in inputs and outputs of blocktypes, e.g., `Sheet`, `File`. +- **Builtin blocktypes**: These blocktypes are the very basic building blocks in Jayvee, e.g., `HttpExtractor`, `SqliteLoader`. +- **Builtin constraint types**: These constraint types are constraints with custom logic, e.g., `LengthConstraint`, `RegexConstraint`. + +Builtin definitions are usually generated and added to the standard library from the internal representations of the concepts. + +### User-defined Contents + +The implementations of user-defined contents are expressed in Jayvee itself. Examples: + +- **User-defined valuetypes**: These valuetypes are based on builtin or other user-defined valuetypes. Their definition is expressed natively in Jayvee, e.g., `Percent`. +- **User-defined blocktypes**: These blocktypes are based on builtin or other user-defined blocktypes. Their definition is expressed natively in Jayvee. + +We use `jv` files to add user-defined valuetypes to the standard library (see below). + +## Extending the Standard Library + +Just add `jv` files to the directory [here](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/stdlib/). It is crawled hierarchically, meaning that you can also organize files in folders. + +## Implementation + +### 1. Code generation + +We use code generation to transform these `.jv` files into TypeScript files that the language server can used. The [generation script](https://github.com/jvalue/jayvee/tree/main/tools/scripts/language-server/generate-stdlib.mjs) is run via `npm run generate` next to the AST generation. + +### 2. Builtin libraries + +The solution we chose to implement the standard library mechanism is close to the [builtin library tutorial](https://langium.org/guides/builtin-library/) by Langium. The following components are of interest: + +- [JayveeWorkspaceManager](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/lib/builtin-library/jayvee-workspace-manager.ts) in the `language-server` that registers all libraries with the langium framework. +- [StandardLibraryFileSystemProvider](https://github.com/jvalue/jayvee/tree/main/apps/vs-code-extension/src/standard-library-file-system-provider.ts) in the `vs-code-extension` that registers all libraries with the vscode plugin framework. diff --git a/apps/docs/versioned_docs/version-0.4.0/dev/04-guides/05-standard-library.md.license b/apps/docs/versioned_docs/version-0.4.0/dev/04-guides/05-standard-library.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/dev/04-guides/05-standard-library.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.4.0/dev/04-guides/06-jayvee-extensions.md b/apps/docs/versioned_docs/version-0.4.0/dev/04-guides/06-jayvee-extensions.md new file mode 100644 index 00000000..2b212aa9 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/dev/04-guides/06-jayvee-extensions.md @@ -0,0 +1,190 @@ +--- +title: Jayvee Extensions +sidebar_position: 6 +--- + +## Concepts + +### Jayvee extension + +A Jayvee extension is used to add new block types to the language without having to modify the actual grammar of the language. Such a Jayvee extension usually consists of two parts: a language extension and an execution extension which are described in the upcoming two sections. + +Jayvee extensions that shall be used by default are bundled into the so-called [standard extension](https://github.com/jvalue/jayvee/tree/main/libs/extensions/std). That way, the [language server](https://github.com/jvalue/jayvee/tree/main/libs/language-server) and the [interpreter](https://github.com/jvalue/jayvee/tree/main/apps/interpreter) are able to load them out of the box. + +### Language extension + +A language extension defines meta information of block types which are required by the +[language server](https://github.com/jvalue/jayvee/tree/main/libs/language-server). +Such meta information describes properties of +block types such as their names, input / output types and their properties. + +Note that language extensions don't define any behavior. Instead, this is done by the corresponding execution extension. + +### Execution extension + +An execution extension defines behavior for block types. They build on the meta information from the corresponding +language extension, e.g. input / output types of the block need to match the signature of the execution method and +properties are accessed by their specified name. + +Execution extensions are only required by the [interpreter](https://github.com/jvalue/jayvee/tree/main/apps/interpreter) and not necessarily by the [language server](https://github.com/jvalue/jayvee/tree/main/libs/language-server) as they solely define behavior. + +## Recipes + +### Add a new Jayvee execution extension + +#### 1. Generate an execution libraries + +```bash +npx nx g @nx/node:library --name="extensions/<extension-name>/exec" +``` + +#### 2. Create extension classes + +In `libs/extensions/<extension-name>/exec/src/extension.ts`: + +```typescript +import { + BlockExecutorClass, + JayveeExecExtension, +} from '@jvalue/jayvee-execution'; + +export class MyExecExtension implements JayveeExecExtension { + getBlockExecutors(): BlockExecutorClass[] { + return []; + } +} +``` + +#### 3. Export extension classes + +In `libs/extensions/<extension-name>/exec/src/index.ts`: + +```typescript +export * from './extension'; +``` + +#### 4. Register new extension classes in the standard extension + +In `libs/extensions/std/exec/src/extension.ts`: + +```typescript +// ... + +import { MyExecExtension } from '@jvalue/jayvee-extensions/<extension-name>/exec'; + +export class StdExecExtension implements JayveeExecExtension { + private readonly wrappedExtensions: JayveeExecExtension[] = [ + // ... + // Register your execution extension here: + new MyExecExtension(), + // ... + ]; + + // ... +} +``` + +### Add a new block type in a Jayvee extension + +#### 1. Create a builtin blocktype + +Define the syntax of the new blocktype in the [language server's builtin blocktypes](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/stdlib/builtin-blocktypes). + +The following example defines a block type `MyExtractor` with a text property called `url` and a property `retries` with a default value: + +```jayvee +builtin blocktype MyExtractor { + input default oftype None; + output default oftype Sheet; + + property url oftype text; + property retries oftype interger: 10; +} +``` + +The new block type will be automatically registered on the language server startup. + +#### 2. Add custom validation logic (if required) + +If the block type and/or its properties requires custom validation logic, you can implement it in the [language server's block type specific checks](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/lib/validation/checks/blocktype-specific). + +#### 3. Implement `BlockExecutor` + +The following example implements an executor for the previously defined block type `MyExtractor`. + +The `execute` method defines the behavior when a block is executed. Its signature matches the input and output types defined in `MyExtractor.jv` file. + +In `libs/extensions/<extension-name>/exec/src/lib/my-extractor-executor.ts`: + +```typescript +import * as R from '@jvalue/jayvee-execution'; +import { + AbstractBlockExecutor, + BlockExecutorClass, + ExecutionContext, + Sheet, + implementsStatic, +} from '@jvalue/jayvee-execution'; +import { IOType, PrimitiveValuetypes } from '@jvalue/jayvee-language-server'; + +@implementsStatic<BlockExecutorClass>() +export class MyExtractorExecutor + extends AbstractBlockExecutor<IOType.NONE, IOType.SHEET> +{ + // Needs to match the type in meta information: + public static readonly type = 'MyExtractor'; + + public readonly inputType = IOType.NONE; + public readonly outputType = IOType.SHEET; + + async doExecute( + input: None, + context: ExecutionContext, + ): Promise<R.Result<Sheet>> { + // Accessing property values by their name: + const url = context.getPropertyValue( + 'url', + PrimitiveValuetypes.Text, + ); + const limit = context.getPropertyValue( + 'limit', + PrimitiveValuetypes.Integer, + ); + + // ... + + if (error) { + return R.err(...); + } + + return R.ok(...); + } +} +``` + +> **Info** +> The interface `BlockExecutor<I,O>` is used as an API for block executors. The abstract class `AbstractBlockExecutor<I,O>` gives some further functionality for free, e.g., debug logging. + +> **Warning** +> The generic types of `AbstractBlockExecutor<I,O>` need to match the input and output types of the corresponding `blocktype` definition. + +#### 4. Register the new `BlockExecutor` in the execution extension + +In `libs/extensions/<extension-name>/exec/src/extension.ts`: + +```typescript +// ... + +import { MyExtractorExecutor } from './lib/my-extractor-executor'; + +export class MyExecExtension implements JayveeExecExtension { + getBlockExecutors(): BlockExecutorClass[] { + return [ + // ... + // Register your block executor here: + MyExtractorExecutor, + // ... + ]; + } +} +``` diff --git a/apps/docs/versioned_docs/version-0.4.0/dev/04-guides/06-jayvee-extensions.md.license b/apps/docs/versioned_docs/version-0.4.0/dev/04-guides/06-jayvee-extensions.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/dev/04-guides/06-jayvee-extensions.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.4.0/dev/04-guides/_category_.json b/apps/docs/versioned_docs/version-0.4.0/dev/04-guides/_category_.json new file mode 100644 index 00000000..80f6fce8 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/dev/04-guides/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Guides", + "position": 4, + "link": { + "type": "generated-index", + "description": "Here you can find guides that will help you developing certain aspects of Jayvee." + } +} diff --git a/apps/docs/versioned_docs/version-0.4.0/dev/04-guides/_category_.json.license b/apps/docs/versioned_docs/version-0.4.0/dev/04-guides/_category_.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/dev/04-guides/_category_.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.4.0/dev/05-design-principles.md b/apps/docs/versioned_docs/version-0.4.0/dev/05-design-principles.md new file mode 100644 index 00000000..8b15dd8c --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/dev/05-design-principles.md @@ -0,0 +1,24 @@ +--- +title: Design Principles +sidebar_position: 5 +--- + +When deciding on new features for the domain-specific language itself, we try to adhere to the following high level guidelines. Of course, none of these statements is set in stone and every decision is a tradeoff. + +## Jayvee Manifesto +_Inspired by the [Agile Manifesto](https://agilemanifesto.org/)._ + +We are uncovering better ways of _modeling data pipelines by providing a domain-specific language for data engineering and making it easy for everyone to participate in it_. + +Through this work we have come to value: + +1. **Describing a goal state** over how to get there. +2. **Explicit modeling** over hidden magic. +3. **Composition** over inheritance. +4. **Flat structures** over deep nesting. + +That is, while there is value in the items on the right, we value the items on the left more. + +Through this work, we also have come to explore: + +5. **Libraries** over language features. \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.4.0/dev/05-design-principles.md.license b/apps/docs/versioned_docs/version-0.4.0/dev/05-design-principles.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/dev/05-design-principles.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.4.0/dev/12-jayvee-testing.md b/apps/docs/versioned_docs/version-0.4.0/dev/12-jayvee-testing.md new file mode 100644 index 00000000..1121c1ef --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/dev/12-jayvee-testing.md @@ -0,0 +1,254 @@ +--- +title: Writing tests for Jayvee +--- + +In order to ensure that Jayvee works as intended and to catch breaking changes, we have implemented the following components for regression testing: +- Testing utils: utils to create Langium Typescript objects from *.jv test assets (see [here](#testing-utils)) as well as mocks for execution testing (see [here](#testing-utils-1)) +- [Grammar tests](#grammar-tests): test the grammar parsing and validation +- [Execution tests](#execution-tests): test the execution of blocks + +## Conventions +All of the existing tests follow these conventions: +1. The `<file-name>.spec.ts` file is located next to the `<file-name>.ts` file itself. +2. The `*.jv` assets are located inside a `test/assets/<file-name>` folder. +Take a look at one of the exisiting tests for more details. + +## Grammar tests +These kind of tests are mainly located inside the [language-server](https://github.com/jvalue/jayvee/tree/main/libs/language-server) as well as the language parts of each extension (for example [std/lang](https://github.com/jvalue/jayvee/tree/main/libs/extensions/std/lang)). + +### Testing utils +The testing utils are located inside the `language-server` in a dedicated [test folder](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/test). +These utils can be imported using `@jvalue/jayvee-language-server/test` and contain the following parts: + +[**langium-utils.ts**](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/test/langium-utils.ts): +This utils file contains two functions: +- `parseHelper` to simplify parsing the input (content of a *.jv file) and returning the corresponding `LangiumDocument`, and +- `validationHelper` parse and validate the created document. +They are kept in a separate file due to being copied from the Langium repository and thus subject to a different code license and copyright. + +[**utils.ts**](https://github.com/jvalue/jayvee/blob/main/libs/language-server/src/test/utils.ts): +This file contains custom testing utility utils functions, like `readJvTestAssetHelper` for reading jv test assets. +Example: +``` ts +import * as path from 'path'; + +import { createJayveeServices } from '@jvalue/jayvee-language-server'; +import { + ParseHelperOptions, + expectNoParserAndLexerErrors, + parseHelper, + readJvTestAssetHelper, +} from '@jvalue/jayvee-language-server/test'; +import { AstNode, LangiumDocument } from 'langium'; + +describe('My example test', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../test/assets/', // localized path to test assets folder + ); + + beforeAll(() => { + // [...] register extensions etc + const services = createJayveeServices(NodeFileSystem).Jayvee; // Or retrieve them if services already exist + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + // [...] + + it('My dummy test', () => { + const text = readJvTestAsset('<sub-folder>/<test-asset-name>.jv'); + + const document = await parse(text); + expectNoParserAndLexerErrors(document); + // Rest of test + }); +}); +``` +If you want to simply validate the test assets, simply replace `parseHelper` with `validationHelper` (and adjust the types). +You can find detailed documentation of all the utility functions directly in the code. + +[**extension/**](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/test/extension): +This folder contains a Jayvee extension for testing. +If there are certain blocks required for testing a certain feature, they can be defined here. +One such example is the already defined `TestProperty` block which has a multitude of different properties, each with a different type. +This block is used for testing properties and property-assignments. +The extension provides loader and extractor blocks for all IOTypes without any properties. +These blocks are automatically generated at runtime with the following naming scheme: +`Test${ioType}${io === 'input' ? 'Loader' : 'Extractor'}` (Example: `TestFileExtractor`). +This allows for easy (grammar) testing of non loader/extractor blocks: +``` jv +pipeline Pipeline { + + TestExtractor -> BlockUnderTest -> TestLoader; + + block BlockUnderTest oftype CellWriter { + at: range A1:A3; + write: ['values', 'to', 'write']; + } + + block TestExtractor oftype TestSheetExtractor { } + block TestLoader oftype TestSheetLoader { } +} +``` + +### Existing tests +Currently there are already tests for the following parts: +- Language-server validation checks (located [here](https://github.com/jvalue/jayvee/tree/main/libs/language-server/src/lib/validation)) +- Language-server constraint validation (located [here](https://github.com/jvalue/jayvee/tree/dev/libs/language-server/src/lib/constraint)) +- Custom block (property) validation of the three existing extensions (std extension located [here](https://github.com/jvalue/jayvee/blob/dev/libs/extensions/std/lang/src)) +- Grammar validation tests for all official full examples from the [/example](https://github.com/jvalue/jayvee/tree/main/example) folder (located [here](https://github.com/jvalue/jayvee/blob/dev/libs/extensions/std/lang/src/example-validation.spec.ts)) +- Grammar validation tests for all block examples of the std extension (located [here](https://github.com/jvalue/jayvee/blob/dev/libs/extensions/std/lang/src/meta-inf-example-validation.spec.ts)) + +## Execution tests +These kind of tests are mainly located inside the [interpreter](https://github.com/jvalue/jayvee/tree/main/libs/language-server), the [interpreter-lib](https://github.com/jvalue/jayvee/tree/dev/libs/interpreter-lib), the [execution lib](https://github.com/jvalue/jayvee/tree/dev/libs/execution) as well as the execution parts of each extension (for example [std/exec](https://github.com/jvalue/jayvee/tree/main/libs/extensions/std/exec)). + +### Testing utils +The testing utils for execution tests are spread between the extensions, with the interfaces and base utils located inside the [execution lib](https://github.com/jvalue/jayvee/tree/dev/libs/execution). +They can be imported using `@jvalue/jayvee-extensions/rdbms/test`, `@jvalue/jayvee-extensions/std/test` and `@jvalue/jayvee-execution/test`. + +[**utils.ts**](https://github.com/jvalue/jayvee/blob/dev/libs/execution/test/utils.ts): +At the moment this only contains two functions: +- `clearBlockExecutorRegistry` for clearing the registry containing all `BlockExecutor`s, and +- `clearConstraintExecutorRegistry` clearing the corresponding `ConstraintExecutor`s registry. +They are required in case the tested method initializes Jayvee itself (see [smoke test](#existing-tests-1)). + +[**test-logger.ts**](https://github.com/jvalue/jayvee/blob/dev/libs/execution/test/test-logger.ts): +This contains a subclass of the [`DefaultLogger`](https://github.com/jvalue/jayvee/blob/dev/libs/execution/src/lib/logging/default-logger.ts) used for tests which require a `Logger` implementation. The `TestLogger` contains the following tests functionality: +- `getLogs`: retrieve the cached logs that the logger received. +- `clearLogs`: clear the cached logs. + +[**block-executor-mocks.ts**](https://github.com/jvalue/jayvee/blob/dev/libs/execution/test/block-executor-mock.ts): +`BlockExecutorMock` interface for defining mocks for `AbstractBlockExecutor`. Generally only loader and executor blocks require mocks, because they interact with "the outside world" (i.e. `HttpExtractor` making http calls). +Due to how vastly different each `BlockExecutor` can be, this interface is very simple, containing only a `setup(...args: unknown[])` and a `restore()` method. See below for existing implementations. + +[**rdbms/exec/test**](https://github.com/jvalue/jayvee/tree/dev/libs/extensions/rdbms/exec/test): +Contains the implementation of `BlockExecutorMock` for `PostgresLoaderExecutor` and `SQLiteLoaderExecutor`. +Both of these executors are mocked using `jest.mock` to mock the corresponding libraries (`pg` and `sqlite3`) +**Usage:** +``` ts +import { + PostgresLoaderExecutorMock, + SQLiteLoaderExecutorMock, +} from '@jvalue/jayvee-extensions/rdbms/test'; + +// Global mocking of external library at the top of test file required, +// even though the mocking is encapsulated in helper classes +jest.mock('pg', () => { + const mClient = { + connect: jest.fn(), + query: jest.fn(), + end: jest.fn(), + }; + return { Client: jest.fn(() => mClient) }; +}); +jest.mock('sqlite3', () => { + const mockDB = { + close: jest.fn(), + run: jest.fn(), + }; + return { Database: jest.fn(() => mockDB) }; +}); + +describe('Dummy describe', () => { + // [...] + + let postgresLoaderMock: PostgresLoaderExecutorMock; + let sqliteLoaderMock: SQLiteLoaderExecutorMock; + + beforeAll(() => { + postgresLoaderMock = new PostgresLoaderExecutorMock(); + sqliteLoaderMock = new SQLiteLoaderExecutorMock(); + }); + + afterEach(() => { + postgresLoaderMock.restore(); + sqliteLoaderMock.restore(); + }); + + it('Dummy test', async () => { + // Prepare mocks + postgresLoaderMock.setup(); + sqliteLoaderMock.setup(); + + // [...] execute test + + expect(postgresLoaderMock.pgClient.connect).toBeCalledTimes(1); + expect(postgresLoaderMock.pgClient.query).toBeCalledTimes(1); + expect(postgresLoaderMock.pgClient.end).toBeCalledTimes(1); + expect(sqliteLoaderMock.sqliteClient.run).toBeCalledTimes(1); + expect(sqliteLoaderMock.sqliteClient.close).toBeCalledTimes(1); + }); +}); +``` + +[**std/exec/test/mocks**](https://github.com/jvalue/jayvee/tree/dev/libs/extensions/std/exec/test): +Contains the implementation of `BlockExecutorMock` for `HttpExtractorExecutorMock`. +This implementation uses [nock](https://www.npmjs.com/package/nock) for mocking HTTP(S) responses. +The `setup` method is further specified requiring one parameter `registerMocks: () => Array<nock.Scope>`, which returns all used `nock.Scope` (i.e. the return value of `nock('<URL>')`), see usage below: +**Usage:** +``` ts +import * as path from 'path'; + +import { HttpExtractorExecutorMock } from '@jvalue/jayvee-extensions/std/test'; + +describe('Dummy describe', () => { + // [...] + + let httpExtractorMock: HttpExtractorExecutorMock; + + beforeAll(() => { + httpExtractorMock = new HttpExtractorExecutorMock(); + }); + + afterEach(() => { + httpExtractorMock.restore(); + }); + + it('should have no errors when executing gtfs-static-and-rt.jv example', async () => { + // Prepare mocks + httpExtractorMock.setup(() => { + return [ + nock( + '<URL_1>', + ) + .get('<PATH>') + .replyWithFile( + 200, + path.resolve(__dirname, '../test/assets/file1.zip'), + { + 'Content-Type': 'application/octet-stream', + }, + ), + nock('<URL_2>') + .get('<PATH_1>') + .replyWithFile( + 200, + path.resolve( + __dirname, + '../test/assets/file2', + ), + { + 'Content-Type': 'application/octet-stream', + }, + ) + .get('<PATH_2>') + .reply(200, { content: "My dummy json reply." }), + ]; + }) + + // [...] execute test + + expect(httpExtractorMock.nockScopes.every((scope) => scope.isDone())); + }); +}); +``` + +### Existing tests +Currently there are already tests for the following parts: +- Smoke test for official examples (located [here](https://github.com/jvalue/jayvee/blob/dev/apps/interpreter/src/examples-smoke-test.spec.ts)) diff --git a/apps/docs/versioned_docs/version-0.4.0/dev/12-jayvee-testing.md.license b/apps/docs/versioned_docs/version-0.4.0/dev/12-jayvee-testing.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/dev/12-jayvee-testing.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.4.0/user/block-types/ArchiveInterpreter.md b/apps/docs/versioned_docs/version-0.4.0/user/block-types/ArchiveInterpreter.md new file mode 100644 index 00000000..2a622e27 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/block-types/ArchiveInterpreter.md @@ -0,0 +1,33 @@ +--- +title: ArchiveInterpreter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `File` + +Output type: `FileSystem` + +## Description + +Interprets a `File` as an archive file and converts it to a `FileSystem`. The archive file root is considered the root of the `FileSystem`. + +## Example 1 + +```jayvee + block ZipArchiveInterpreter oftype ArchiveInterpreter { + archiveType: "zip"; + } +``` + +Interprets a `File` as a ZIP-archive and creates a `FileSystem` of its extracted contents. + +## Properties + +### `archiveType` + +Type `text` + +#### Description + +The archive type to be interpreted, e.g., "zip" or "gz". diff --git a/apps/docs/versioned_docs/version-0.4.0/user/block-types/ArchiveInterpreter.md.license b/apps/docs/versioned_docs/version-0.4.0/user/block-types/ArchiveInterpreter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/block-types/ArchiveInterpreter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.4.0/user/block-types/CSVInterpreter.md b/apps/docs/versioned_docs/version-0.4.0/user/block-types/CSVInterpreter.md new file mode 100644 index 00000000..9242e045 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/block-types/CSVInterpreter.md @@ -0,0 +1,55 @@ +--- +title: CSVInterpreter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `TextFile` + +Output type: `Sheet` + +## Description + +Interprets an input file as a csv-file containing string-values delimited by `delimiter` and outputs a `Sheet`. + +## Example 1 + +```jayvee + block AgencyCSVInterpreter oftype CSVInterpreter { + delimiter: ";"; + } +``` + +Interprets an input file as a csv-file containing string-values delimited by `;` and outputs `Sheet`. + +## Properties + +### `delimiter` + +Type `text` + +Default: `","` + +#### Description + +The delimiter for values in the CSV file. + +### `enclosing` + +Type `text` + +Default: `""` + +#### Description + +The enclosing character that may be used for values in the CSV file. + +### `enclosingEscape` + +Type `text` + +Default: `""` + +#### Description + +The character to escape enclosing characters in values. diff --git a/apps/docs/versioned_docs/version-0.4.0/user/block-types/CSVInterpreter.md.license b/apps/docs/versioned_docs/version-0.4.0/user/block-types/CSVInterpreter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/block-types/CSVInterpreter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.4.0/user/block-types/CellRangeSelector.md b/apps/docs/versioned_docs/version-0.4.0/user/block-types/CellRangeSelector.md new file mode 100644 index 00000000..2e55f64c --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/block-types/CellRangeSelector.md @@ -0,0 +1,33 @@ +--- +title: CellRangeSelector +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Sheet` + +Output type: `Sheet` + +## Description + +Selects a subset of a `Sheet` to produce a new `Sheet`. + +## Example 1 + +```jayvee + block CarsCoreDataSelector oftype CellRangeSelector { + select: range A1:E*; + } +``` + +Selects the cells in the given range and produces a new `Sheet` containing only the selected cells. + +## Properties + +### `select` + +Type `CellRange` + +#### Description + +The cell range to select. diff --git a/apps/docs/versioned_docs/version-0.4.0/user/block-types/CellRangeSelector.md.license b/apps/docs/versioned_docs/version-0.4.0/user/block-types/CellRangeSelector.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/block-types/CellRangeSelector.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.4.0/user/block-types/CellWriter.md b/apps/docs/versioned_docs/version-0.4.0/user/block-types/CellWriter.md new file mode 100644 index 00000000..e397dccf --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/block-types/CellWriter.md @@ -0,0 +1,53 @@ +--- +title: CellWriter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Sheet` + +Output type: `Sheet` + +## Description + +Writes textual values into cells of a `Sheet`. The number of text values needs to match the number of cells to write into. + +## Example 1 + +```jayvee + block NameHeaderWriter oftype CellWriter { + at: cell A1; + write: ["Name"]; + } +``` + +Write the value "Name" into cell `A1`. + +## Example 2 + +```jayvee + block HeaderSequenceWriter oftype CellWriter { + at: range A1:A2; + write: ["Name", "Age"]; + } +``` + +Write the values "Name", "Age" into cells `A1` and `A2`. + +## Properties + +### `write` + +Type `Collection<text>` + +#### Description + +The values to write. + +### `at` + +Type `CellRange` + +#### Description + +The cells to write into. diff --git a/apps/docs/versioned_docs/version-0.4.0/user/block-types/CellWriter.md.license b/apps/docs/versioned_docs/version-0.4.0/user/block-types/CellWriter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/block-types/CellWriter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.4.0/user/block-types/ColumnDeleter.md b/apps/docs/versioned_docs/version-0.4.0/user/block-types/ColumnDeleter.md new file mode 100644 index 00000000..affce276 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/block-types/ColumnDeleter.md @@ -0,0 +1,33 @@ +--- +title: ColumnDeleter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Sheet` + +Output type: `Sheet` + +## Description + +Deletes columns from a `Sheet`. Column IDs of subsequent columns will be shifted accordingly, so there will be no gaps. + +## Example 1 + +```jayvee + block MpgColumnDeleter oftype ColumnDeleter { + delete: [column B]; + } +``` + +Deletes column B (i.e. the second column). + +## Properties + +### `delete` + +Type `Collection<CellRange>` + +#### Description + +The columns to delete. Has to be a full column. diff --git a/apps/docs/versioned_docs/version-0.4.0/user/block-types/ColumnDeleter.md.license b/apps/docs/versioned_docs/version-0.4.0/user/block-types/ColumnDeleter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/block-types/ColumnDeleter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.4.0/user/block-types/FilePicker.md b/apps/docs/versioned_docs/version-0.4.0/user/block-types/FilePicker.md new file mode 100644 index 00000000..9fd0d5ad --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/block-types/FilePicker.md @@ -0,0 +1,33 @@ +--- +title: FilePicker +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `FileSystem` + +Output type: `File` + +## Description + +Selects one `File` from a `FileSystem` based on its relative path to the root of the `FileSystem`. If no file matches the relative path, no output is created and the execution of the pipeline is aborted. + +## Example 1 + +```jayvee + block AgencyFilePicker oftype FilePicker { + path: "./agency.txt"; + } +``` + +Tries to pick the file `agency.txt` from the root of the provided `FileSystem`. If `agency.txt` exists it is passed on as `File`, if it does not exist the execution of the pipeline is aborted. + +## Properties + +### `path` + +Type `text` + +#### Description + +The path of the file to select, relative to the root of the provided `FileSystem`. diff --git a/apps/docs/versioned_docs/version-0.4.0/user/block-types/FilePicker.md.license b/apps/docs/versioned_docs/version-0.4.0/user/block-types/FilePicker.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/block-types/FilePicker.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.4.0/user/block-types/GtfsRTInterpreter.md b/apps/docs/versioned_docs/version-0.4.0/user/block-types/GtfsRTInterpreter.md new file mode 100644 index 00000000..3da451b2 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/block-types/GtfsRTInterpreter.md @@ -0,0 +1,76 @@ +--- +title: GtfsRTInterpreter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `File` + +Output type: `Sheet` + +## Description + +Interprets an protobuf file (binary) of type `File` by decoding the file according to `gtfs-realtime.proto`. Outputs the extracted entity defined by `entity` as a `Sheet` + +## Example 1 + +```jayvee + block GtfsRTTripUpdateInterpreter oftype GtfsRTInterpreter{ + entity: "trip_update"; + } +``` + +A file is interpretet as an GTFS-RT file, which contains TripUpdate. + +## Properties + +### `entity` + +Type `text` + +#### Description + +Entity to process from GTFS-RT-feed (`trip_update`, `alert` or `vehicle`). + We currently support following Output-Sheets, each are an equivalent to the flattened Element Index defined in <https://developers.google.com/transit/gtfs-realtime/reference#element-index> (just required fields are included): + Entity TripUpdate: + ``` + [ + 'header.gtfs_realtime_version', + 'header.timestamp', + 'header.incrementality', + 'entity.id', + 'entity.trip_update.trip.trip_id', + 'entity.trip_update.trip.route_id', + 'entity.trip_update.stop_time_update.stop_sequence', + 'entity.trip_update.stop_time_update.stop_id', + 'entity.trip_update.stop_time_update.arrival.time', + 'entity.trip_update.stop_time_update.departure.time', + ]; + ``` + Entity VehiclePosition: + ``` + [ + 'header.gtfs_realtime_version', + 'header.timestamp', + 'header.incrementality', + 'entity.id', + 'entity.vehicle_position.vehicle_descriptor.id', + 'entity.vehicle_position.trip.trip_id', + 'entity.vehicle_position.trip.route_id', + 'entity.vehicle_position.position.latitude', + 'entity.vehicle_position.position.longitude', + 'entity.vehicle_position.timestamp', + ]; + ``` + Entity Alert: + ``` + [ + 'header.gtfs_realtime_version', + 'header.timestamp', + 'header.incrementality', + 'entity.id', + 'entity.alert.informed_entity.route_id', + 'entity.alert.header_text', + 'entity.alert.description_text', + ]; + ``` diff --git a/apps/docs/versioned_docs/version-0.4.0/user/block-types/GtfsRTInterpreter.md.license b/apps/docs/versioned_docs/version-0.4.0/user/block-types/GtfsRTInterpreter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/block-types/GtfsRTInterpreter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.4.0/user/block-types/HttpExtractor.md b/apps/docs/versioned_docs/version-0.4.0/user/block-types/HttpExtractor.md new file mode 100644 index 00000000..523e4eb3 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/block-types/HttpExtractor.md @@ -0,0 +1,73 @@ +--- +title: HttpExtractor +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `None` + +Output type: `File` + +## Description + +Extracts a `File` from the web. + +## Example 1 + +```jayvee + block CarsFileExtractor oftype HttpExtractor { + url: "tinyurl.com/4ub9spwz"; + } +``` + +Fetches a file from the given URL. + +## Properties + +### `url` + +Type `text` + +#### Description + +The URL to the file in the web to extract. + +### `retries` + +Type `integer` + +Default: `0` + +#### Description + +Configures how many retries should be executed after a failure fetching the data. + +### `retryBackoffMilliseconds` + +Type `integer` + +Default: `1000` + +#### Description + +Configures the wait time in milliseconds before executing a retry. + +### `retryBackoffStrategy` + +Type `text` + +Default: `"exponential"` + +#### Description + +Configures the wait strategy before executing a retry. Can have values "exponential" or "linear". + +### `followRedirects` + +Type `boolean` + +Default: `true` + +#### Description + +Indicates, whether to follow redirects on get requests. If `false`, redirects are not followed. Default `true` diff --git a/apps/docs/versioned_docs/version-0.4.0/user/block-types/HttpExtractor.md.license b/apps/docs/versioned_docs/version-0.4.0/user/block-types/HttpExtractor.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/block-types/HttpExtractor.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.4.0/user/block-types/LocalFileExtractor.md b/apps/docs/versioned_docs/version-0.4.0/user/block-types/LocalFileExtractor.md new file mode 100644 index 00000000..24dcd7c7 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/block-types/LocalFileExtractor.md @@ -0,0 +1,33 @@ +--- +title: LocalFileExtractor +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `None` + +Output type: `File` + +## Description + +Extracts a `File` from the local file system. + +## Example 1 + +```jayvee + block CarsFileExtractor oftype LocalFileExtractor { + filePath: "cars.csv"; + } +``` + +Extracts a file from the given path on the local file system. + +## Properties + +### `filePath` + +Type `text` + +#### Description + +The path to the file in the local file system to extract. Path can not traverse up the directory tree. diff --git a/apps/docs/versioned_docs/version-0.4.0/user/block-types/LocalFileExtractor.md.license b/apps/docs/versioned_docs/version-0.4.0/user/block-types/LocalFileExtractor.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/block-types/LocalFileExtractor.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.4.0/user/block-types/PostgresLoader.md b/apps/docs/versioned_docs/version-0.4.0/user/block-types/PostgresLoader.md new file mode 100644 index 00000000..91782ab5 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/block-types/PostgresLoader.md @@ -0,0 +1,78 @@ +--- +title: PostgresLoader +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Table` + +Output type: `None` + +## Description + +Loads a `Table` into a PostgreSQL database sink. + +## Example 1 + +```jayvee + block CarsLoader oftype PostgresLoader { + host: "localhost"; + port: 5432; + username: "postgres"; + password: "postgres"; + database: "CarsDB"; + table: "Cars"; + } +``` + +A local Postgres instance is filled with table data about cars. + +## Properties + +### `host` + +Type `text` + +#### Description + +The hostname or IP address of the Postgres database. + +### `port` + +Type `integer` + +#### Description + +The port of the Postgres database. + +### `username` + +Type `text` + +#### Description + +The username to login to the Postgres database. + +### `password` + +Type `text` + +#### Description + +The password to login to the Postgres database. + +### `database` + +Type `text` + +#### Description + +The database to use. + +### `table` + +Type `text` + +#### Description + +The name of the table to write into. diff --git a/apps/docs/versioned_docs/version-0.4.0/user/block-types/PostgresLoader.md.license b/apps/docs/versioned_docs/version-0.4.0/user/block-types/PostgresLoader.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/block-types/PostgresLoader.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.4.0/user/block-types/RowDeleter.md b/apps/docs/versioned_docs/version-0.4.0/user/block-types/RowDeleter.md new file mode 100644 index 00000000..20093b18 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/block-types/RowDeleter.md @@ -0,0 +1,33 @@ +--- +title: RowDeleter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Sheet` + +Output type: `Sheet` + +## Description + +Deletes one or more rows from a `Sheet`. Row IDs of subsequent rows will be shifted accordingly, so there will be no gaps. + +## Example 1 + +```jayvee + block SecondRowDeleter oftype RowDeleter { + delete: [row 2]; + } +``` + +Deletes row 2 (i.e. the second row). + +## Properties + +### `delete` + +Type `Collection<CellRange>` + +#### Description + +The rows to delete. diff --git a/apps/docs/versioned_docs/version-0.4.0/user/block-types/RowDeleter.md.license b/apps/docs/versioned_docs/version-0.4.0/user/block-types/RowDeleter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/block-types/RowDeleter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.4.0/user/block-types/SQLiteLoader.md b/apps/docs/versioned_docs/version-0.4.0/user/block-types/SQLiteLoader.md new file mode 100644 index 00000000..d48e6f0f --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/block-types/SQLiteLoader.md @@ -0,0 +1,52 @@ +--- +title: SQLiteLoader +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Table` + +Output type: `None` + +## Description + +Loads a `Table` into a SQLite database sink. + +## Example 1 + +```jayvee + block CarsLoader oftype SQLiteLoader { + table: "cars"; + file: "./cars.db"; + } +``` + +A SQLite file `cars.db` is created in the working directory. Incoming data is written to the table `cars`. + +## Properties + +### `table` + +Type `text` + +#### Description + +The name of the table to write into. + +### `file` + +Type `text` + +#### Description + +The path to a SQLite file that will be created if it does not exist. Usual file extensions are `.sqlite` and `.db`. + +### `dropTable` + +Type `boolean` + +Default: `true` + +#### Description + +Indicates, whether to drop the table before loading data into it. If `false`, data is appended to the table instead of dropping it. diff --git a/apps/docs/versioned_docs/version-0.4.0/user/block-types/SQLiteLoader.md.license b/apps/docs/versioned_docs/version-0.4.0/user/block-types/SQLiteLoader.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/block-types/SQLiteLoader.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.4.0/user/block-types/SheetPicker.md b/apps/docs/versioned_docs/version-0.4.0/user/block-types/SheetPicker.md new file mode 100644 index 00000000..26710170 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/block-types/SheetPicker.md @@ -0,0 +1,33 @@ +--- +title: SheetPicker +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Workbook` + +Output type: `Sheet` + +## Description + +Selects one `Sheet` from a `Workbook` based on its `sheetName`. If no sheet matches the name, no output is created and the execution of the pipeline is aborted. + +## Example 1 + +```jayvee + block AgencySheetPicker oftype SheetPicker { + sheetName: "AgencyNames"; + } +``` + +Tries to pick the sheet `AgencyNames` from the provided `Workbook`. If `AgencyNames` exists it is passed on as `Sheet`, if it does not exist the execution of the pipeline is aborted. + +## Properties + +### `sheetName` + +Type `text` + +#### Description + +The name of the sheet to select. diff --git a/apps/docs/versioned_docs/version-0.4.0/user/block-types/SheetPicker.md.license b/apps/docs/versioned_docs/version-0.4.0/user/block-types/SheetPicker.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/block-types/SheetPicker.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.4.0/user/block-types/TableInterpreter.md b/apps/docs/versioned_docs/version-0.4.0/user/block-types/TableInterpreter.md new file mode 100644 index 00000000..7d81aae3 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/block-types/TableInterpreter.md @@ -0,0 +1,63 @@ +--- +title: TableInterpreter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Sheet` + +Output type: `Table` + +## Description + +Interprets a `Sheet` as a `Table`. In case a header row is present in the sheet, its names can be matched with the provided column names. Otherwise, the provided column names are assigned in order. + +## Example 1 + +```jayvee + block CarsTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "name" oftype text, + "mpg" oftype decimal, + "cyl" oftype integer, + ]; + } +``` + +Interprets a `Sheet` about cars with a topmost header row and interprets it as a `Table` by assigning a primitive valuetype to each column. The column names are matched to the header, so the order of the type assignments does not matter. + +## Example 2 + +```jayvee + block CarsTableInterpreter oftype TableInterpreter { + header: false; + columns: [ + "name" oftype text, + "mpg" oftype decimal, + "cyl" oftype integer, + ]; + } +``` + +Interprets a `Sheet` about cars without a topmost header row and interprets it as a `Table` by sequentially assigning a name and a primitive valuetype to each column of the sheet. Note that the order of columns matters here. The first column (column `A`) will be named "name", the second column (column `B`) will be named "mpg" etc. + +## Properties + +### `header` + +Type `boolean` + +Default: `true` + +#### Description + +Whether the first row should be interpreted as header row. + +### `columns` + +Type `Collection<ValuetypeAssignment>` + +#### Description + +Collection of valuetype assignments. Uses column names (potentially matched with the header or by sequence depending on the `header` property) to assign a primitive valuetype to each column. diff --git a/apps/docs/versioned_docs/version-0.4.0/user/block-types/TableInterpreter.md.license b/apps/docs/versioned_docs/version-0.4.0/user/block-types/TableInterpreter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/block-types/TableInterpreter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.4.0/user/block-types/TableTransformer.md b/apps/docs/versioned_docs/version-0.4.0/user/block-types/TableTransformer.md new file mode 100644 index 00000000..eb52dc90 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/block-types/TableTransformer.md @@ -0,0 +1,73 @@ +--- +title: TableTransformer +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `Table` + +Output type: `Table` + +## Description + +Applies a transform on each value of a column. The input port type of the used transform has to match the type of the input column. + +## Example 1 + +```jayvee + transform CelsiusToFahrenheit { + from Celsius oftype decimal; + to Fahrenheit oftype decimal; + Fahrenheit: (Celsius * 9/5) + 32; + } + block CelsiusToFahrenheitTransformer oftype TableTransformer { + inputColumns: ['temperature']; + outputColumn: 'temperature'; + use: CelsiusToFahrenheit; + } +``` + +Given a column "temperature" with temperature values in Celsius, it overwrites the column with computed values in Fahrenheit by using the `CelsiusToFahrenheit` transform. The transform itself is defined elsewhere in the model. + +## Example 2 + +```jayvee + transform CelsiusToFahrenheit { + from Celsius oftype decimal; + to Fahrenheit oftype decimal; + Fahrenheit: (Celsius * 9/5) + 32; + } + block CelsiusToFahrenheitTransformer oftype TableTransformer { + inputColumns: ['temperatureCelsius']; + outputColumn: 'temperatureFahrenheit'; + use: CelsiusToFahrenheit; + } +``` + +Given a column "temperatureCelsius" with temperature values in Celsius, it adds a new column "temperatureFahrenheit" with computed values in Fahrenheit by using the `CelsiusToFahrenheit` transform. The transform itself is defined elsewhere in the model. + +## Properties + +### `inputColumns` + +Type `Collection<text>` + +#### Description + +The names of the input columns. The columns have to be present in the table and match with the transform's input port types. + +### `outputColumn` + +Type `text` + +#### Description + +The name of the output column. Overwrites the column if it already exists, or otherwise creates a new one. + +### `use` + +Type `Transform` + +#### Description + +Reference to the transform that is applied to the column. diff --git a/apps/docs/versioned_docs/version-0.4.0/user/block-types/TableTransformer.md.license b/apps/docs/versioned_docs/version-0.4.0/user/block-types/TableTransformer.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/block-types/TableTransformer.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.4.0/user/block-types/TextFileInterpreter.md b/apps/docs/versioned_docs/version-0.4.0/user/block-types/TextFileInterpreter.md new file mode 100644 index 00000000..0fefdcc2 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/block-types/TextFileInterpreter.md @@ -0,0 +1,35 @@ +--- +title: TextFileInterpreter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `File` + +Output type: `TextFile` + +## Description + +Interprets a `File` as a `TextFile`. + +## Properties + +### `encoding` + +Type `text` + +Default: `"utf-8"` + +#### Description + +The encoding used for decoding the file contents. + +### `lineBreak` + +Type `Regex` + +Default: `{}` + +#### Description + +The regex for identifying line breaks. diff --git a/apps/docs/versioned_docs/version-0.4.0/user/block-types/TextFileInterpreter.md.license b/apps/docs/versioned_docs/version-0.4.0/user/block-types/TextFileInterpreter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/block-types/TextFileInterpreter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.4.0/user/block-types/TextLineDeleter.md b/apps/docs/versioned_docs/version-0.4.0/user/block-types/TextLineDeleter.md new file mode 100644 index 00000000..b36a46c4 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/block-types/TextLineDeleter.md @@ -0,0 +1,23 @@ +--- +title: TextLineDeleter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `TextFile` + +Output type: `TextFile` + +## Description + +Deletes individual lines from a `TextFile`. + +## Properties + +### `lines` + +Type `Collection<integer>` + +#### Description + +The line numbers to delete. diff --git a/apps/docs/versioned_docs/version-0.4.0/user/block-types/TextLineDeleter.md.license b/apps/docs/versioned_docs/version-0.4.0/user/block-types/TextLineDeleter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/block-types/TextLineDeleter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.4.0/user/block-types/TextRangeSelector.md b/apps/docs/versioned_docs/version-0.4.0/user/block-types/TextRangeSelector.md new file mode 100644 index 00000000..75f5e68e --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/block-types/TextRangeSelector.md @@ -0,0 +1,36 @@ +--- +title: TextRangeSelector +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `TextFile` + +Output type: `TextFile` + +## Description + +Selects a range of lines from a `TextFile`. + +## Properties + +### `lineFrom` + +Type `integer` + +Default: `1` + +#### Description + +Inclusive beginning line number for the selection. + +### `lineTo` + +Type `integer` + +Default: `9007199254740991` + +#### Description + +Inclusive ending line number for the selection. + The default value is the biggest usable integer. diff --git a/apps/docs/versioned_docs/version-0.4.0/user/block-types/TextRangeSelector.md.license b/apps/docs/versioned_docs/version-0.4.0/user/block-types/TextRangeSelector.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/block-types/TextRangeSelector.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.4.0/user/block-types/XLSXInterpreter.md b/apps/docs/versioned_docs/version-0.4.0/user/block-types/XLSXInterpreter.md new file mode 100644 index 00000000..3dd9a3c9 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/block-types/XLSXInterpreter.md @@ -0,0 +1,23 @@ +--- +title: XLSXInterpreter +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Input type: `File` + +Output type: `Workbook` + +## Description + +Interprets an input file as a XLSX-file and outputs a `Workbook` containing `Sheet`s. + +## Example 1 + +```jayvee + block AgencyXLSXInterpreter oftype XLSXInterpreter { } +``` + +Interprets an input file as a XLSX-file and outputs a `Workbook` containing `Sheet`s. + +## Properties diff --git a/apps/docs/versioned_docs/version-0.4.0/user/block-types/XLSXInterpreter.md.license b/apps/docs/versioned_docs/version-0.4.0/user/block-types/XLSXInterpreter.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/block-types/XLSXInterpreter.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.4.0/user/block-types/_category_.json b/apps/docs/versioned_docs/version-0.4.0/user/block-types/_category_.json new file mode 100644 index 00000000..740dd2f2 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/block-types/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Block Types", + "position": 3, + "link": { + "type": "generated-index", + "description": "These blocks are shipped with Jayvee and are available right out of the box." + } +} diff --git a/apps/docs/versioned_docs/version-0.4.0/user/block-types/_category_.json.license b/apps/docs/versioned_docs/version-0.4.0/user/block-types/_category_.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/block-types/_category_.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.4.0/user/composite-blocks.md b/apps/docs/versioned_docs/version-0.4.0/user/composite-blocks.md new file mode 100644 index 00000000..3db9e78f --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/composite-blocks.md @@ -0,0 +1,115 @@ +--- +sidebar_position: 4 +--- + +# Composite Blocks + +Composite blocks are a way to create new blocktypes in Jayvee by combining the functionality of existing blocks and pipes. By relying on composite blocks instead of implementing more builtin blocks in a language interpreter, Jayvee supports easy extension by users. + +Composite blocks define: +- with the `property` keyword: properties with a name and [value type](./core-concepts.md#valuetypes), optionally a default value +- with the `input` keyword: one input with a name and io type (that can be None) +- with the `output` keyword: one output with a name and io type (that can be None) +- one pipeline definition, starting from the input (using its name) and ending in the output (again using its name) +- all blocks that are used in the pipeline definition (either builtin or other composite blocks) + +## Example +As an example, the common use-case of extracting a CSV file from a webserver using HTTP. With builtin blocks, a pipeline would start with a HttpExtractor source that downloads a file from the internet and outputs a binary file. This file must be interpreted as text (using a TextFileInterpreter) and finally as Sheet (using a CSVInterpreter). + +### Implementation with builtin blocks +```mermaid +flowchart LR + A[HttpExtractor] --> B(TextFileInterpreter) + B --> C(CSVInterpreter) + C --> D(TableInterpreter) + D --> E[SQLiteSink] +``` + +A pipeline with builtin blocks is very verbose: + +```jayvee +pipeline CarsPipeline { + CarsExtractor + -> CarsTextFileInterpreter + -> CarsCSVInterpreter + -> CarsTableInterpreter + -> CarsLoader; + + block CarsExtractor oftype HttpExtractor { + url: "https://example.com/cars.csv"; + } + + block CarsTextFileInterpreter oftype TextFileInterpreter { } + + block CarsCSVInterpreter oftype CSVInterpreter { + enclosing: '"'; + } + // ... further block definitions +} +``` + +### Refactoring using composite blocks + +The common use-case of downloading a CSV file using HTTP can be refactored into a composite block. Note that we define all properties of the builtin blocks that are used as properties of the new CSVExtractor blocktype (but add fallback values). If some internal configuration is always the same, we could also not expose it as a property of the new blocktype. + +```jayvee +// Define a new blocktype named CSVExtractor outside of the pipeline +composite blocktype CSVExtractor { + // Properties of the CSVExtractor, some with default values + property url oftype text; + property delimiter oftype text: ','; + property enclosing oftype text: ''; + property enclosingEscape oftype text: ''; + + // Input and outputs + input inputName oftype None; + output outputName oftype Sheet; + + // Pipeline definition from input, over blocks defined later, to output + inputName + ->FileExtractor + ->FileTextInterpreter + ->FileCSVInterpreter + ->outputName; + + // Block definitions using values from properties by name + block FileExtractor oftype HttpExtractor { url: url; } + block FileTextInterpreter oftype TextFileInterpreter {} + + block FileCSVInterpreter oftype CSVInterpreter { + delimiter: delimiter; + enclosing: enclosing; + enclosingEscape: enclosingEscape; + } +} +``` + +With the new CSVExtractor composite blocktype, the pipeline now looks like this. + +```mermaid +flowchart LR + CSVExtractor --> D(TableInterpreter) + D --> E[SQLiteSink] + + subgraph CSVExtractor + A[HttpExtractor] --> B(TextFileInterpreter) + B --> C(CSVInterpreter) +end +``` + +If the CSVExtractor is available in the scope of the `CarsPipeline` from before (e.g., by defining it above the pipeline), it can then be used to shorten the actual pipeline code. + +```jayvee +pipeline CarsPipeline { + // HttpExtractor, TextFileInterpreter and CSVInterpreter have been replaced by CSVExtractor + CarsExtractor + -> CarsTableInterpreter + -> CarsLoader; + + block CarsExtractor oftype CSVExtractor { + url: "https://example.com/cars.csv"; + } + + // ... further block definitions +} +``` \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.4.0/user/composite-blocks.md.license b/apps/docs/versioned_docs/version-0.4.0/user/composite-blocks.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/composite-blocks.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.4.0/user/constraint-types/AllowlistConstraint.md b/apps/docs/versioned_docs/version-0.4.0/user/constraint-types/AllowlistConstraint.md new file mode 100644 index 00000000..f4710e00 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/constraint-types/AllowlistConstraint.md @@ -0,0 +1,27 @@ +--- +title: AllowlistConstraint +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Compatible ValueType: text + +## Description + +Limits the values to a defined a set of allowed values. Only values in the list are valid. + +## Example 1 + +```jayvee + constraint TimeUnitString oftype AllowlistConstraint { + allowlist: ["ms", "s", "min", "h", "d", "m", "y"]; + } +``` + +Only allows the common abbreviations for millisecond, second, minute, etc.. + +## Properties + +### `allowlist` + +Type `Collection<text>` diff --git a/apps/docs/versioned_docs/version-0.4.0/user/constraint-types/AllowlistConstraint.md.license b/apps/docs/versioned_docs/version-0.4.0/user/constraint-types/AllowlistConstraint.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/constraint-types/AllowlistConstraint.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.4.0/user/constraint-types/DenylistConstraint.md b/apps/docs/versioned_docs/version-0.4.0/user/constraint-types/DenylistConstraint.md new file mode 100644 index 00000000..91a3da32 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/constraint-types/DenylistConstraint.md @@ -0,0 +1,27 @@ +--- +title: DenylistConstraint +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Compatible ValueType: text + +## Description + +Defines a set of forbidden values. All values in the list are considered invalid. + +## Example 1 + +```jayvee + constraint NoPrimaryColors oftype DenylistConstraint { + denylist: ["red", "blue", "yellow"]; + } +``` + +Denies all primary colors. + +## Properties + +### `denylist` + +Type `Collection<text>` diff --git a/apps/docs/versioned_docs/version-0.4.0/user/constraint-types/DenylistConstraint.md.license b/apps/docs/versioned_docs/version-0.4.0/user/constraint-types/DenylistConstraint.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/constraint-types/DenylistConstraint.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.4.0/user/constraint-types/LengthConstraint.md b/apps/docs/versioned_docs/version-0.4.0/user/constraint-types/LengthConstraint.md new file mode 100644 index 00000000..8c4d3729 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/constraint-types/LengthConstraint.md @@ -0,0 +1,37 @@ +--- +title: LengthConstraint +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Compatible ValueType: text + +## Description + +Limits the length of a string with an upper and/or lower boundary. + Only values with a length within the given range are valid. + +## Example 1 + +```jayvee + constraint ShortAnswerConstraint oftype LengthConstraint { + minLength: 0; + maxLength: 20; + } +``` + +A text constraint with 0 to 20 characters. + +## Properties + +### `minLength` + +Type `integer` + +Default: `0` + +### `maxLength` + +Type `integer` + +Default: `9007199254740991` diff --git a/apps/docs/versioned_docs/version-0.4.0/user/constraint-types/LengthConstraint.md.license b/apps/docs/versioned_docs/version-0.4.0/user/constraint-types/LengthConstraint.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/constraint-types/LengthConstraint.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.4.0/user/constraint-types/RangeConstraint.md b/apps/docs/versioned_docs/version-0.4.0/user/constraint-types/RangeConstraint.md new file mode 100644 index 00000000..8b61b1e0 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/constraint-types/RangeConstraint.md @@ -0,0 +1,60 @@ +--- +title: RangeConstraint +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Compatible ValueType: decimal + +## Description + +Limits the range of a number value with an upper and/or lower boundary which can be inclusive or exclusive. Only values within the given range are considered valid. + +## Example 1 + +```jayvee + constraint HundredScale oftype RangeConstraint { + lowerBound: 1; + upperBound: 100; + } +``` + +A scale between (and including) 1 and 100. + +## Example 2 + +```jayvee + constraint HundredScale oftype RangeConstraint { + lowerBound: 1; + lowerBoundInclusive: false; + upperBound: 100; + } +``` + +A scale for numbers strictly larger than 1 and less or equal to 100. + +## Properties + +### `lowerBound` + +Type `decimal` + +Default: `-9007199254740991` + +### `lowerBoundInclusive` + +Type `boolean` + +Default: `true` + +### `upperBound` + +Type `decimal` + +Default: `9007199254740991` + +### `upperBoundInclusive` + +Type `boolean` + +Default: `true` diff --git a/apps/docs/versioned_docs/version-0.4.0/user/constraint-types/RangeConstraint.md.license b/apps/docs/versioned_docs/version-0.4.0/user/constraint-types/RangeConstraint.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/constraint-types/RangeConstraint.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.4.0/user/constraint-types/RegexConstraint.md b/apps/docs/versioned_docs/version-0.4.0/user/constraint-types/RegexConstraint.md new file mode 100644 index 00000000..b9b5c794 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/constraint-types/RegexConstraint.md @@ -0,0 +1,28 @@ +--- +title: RegexConstraint +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +Compatible ValueType: text + +## Description + +Limits the values complying with a regex. + Only values that comply with the regex are considered valid. + +## Example 1 + +```jayvee + constraint IPv4Format oftype RegexConstraint { + regex: /^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$/; + } +``` + +Text that complies with the IPv4 address format. + +## Properties + +### `regex` + +Type `Regex` diff --git a/apps/docs/versioned_docs/version-0.4.0/user/constraint-types/RegexConstraint.md.license b/apps/docs/versioned_docs/version-0.4.0/user/constraint-types/RegexConstraint.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/constraint-types/RegexConstraint.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.4.0/user/constraint-types/_category_.json b/apps/docs/versioned_docs/version-0.4.0/user/constraint-types/_category_.json new file mode 100644 index 00000000..cd8b1300 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/constraint-types/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Constraint Types", + "position": 6, + "link": { + "type": "generated-index", + "description": "These constraints are shipped with Jayvee and are available right out of the box." + } +} diff --git a/apps/docs/versioned_docs/version-0.4.0/user/constraint-types/_category_.json.license b/apps/docs/versioned_docs/version-0.4.0/user/constraint-types/_category_.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/constraint-types/_category_.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.4.0/user/core-concepts.md b/apps/docs/versioned_docs/version-0.4.0/user/core-concepts.md new file mode 100644 index 00000000..add0a01f --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/core-concepts.md @@ -0,0 +1,89 @@ +--- +sidebar_position: 2 +--- + +# Core Concepts + +The core concepts of Jayvee are `Pipelines`, `Blocks`, and `ValueTypes`. + +## Pipelines + +A `Pipeline` is a sequence of different computing steps, the `Blocks`. +The default output of a block becomes the default input of the next block, building a chain of computing steps. +In the scope of a `Pipeline`, you can connect these blocks via the `pipe` syntax: + +```jayvee +pipeline CarsPipeline { + // Assumption: blocks "GasReserveHttpExtractor", "GasReserveCSVInterpreter", "GasReserveTableInterpreter", and "GasReserveLoader" are defined + + GasReserveHttpExtractor + -> GasReserveTextFileInterpreter + -> GasReserveCSVInterpreter + -> GasReserveTableInterpreter + -> GasReserveLoader; +} +``` + +## Blocks + +A `Block` is a processing step within a `Pipeline`. +It can have a default input and a default output. +We differentiate the following types of `Blocks`: +- `ExtractorBlocks` do not have a default input but only a default output. They model a **data source**. +- `TransformatorBlocks` have a default input and a default output. They model a **transformation**. +- `LoaderBlocks` do have a default input but nor a default output. They model a **data sink**. + +The general structure of a `Pipeline` consisting of different blocks is the following: + +```mermaid +flowchart LR + A[ExtractorBlock] --> B(TransformatorBlock) + B --> C(TransformatorBlock) + C --> D(LoaderBlock) +``` + +The common syntax of blocks is at its core a key-value map to provide configuration to the block. +The availability of property keys and their respective `ValueTypes` is determined by the type of the `Block` - indicated by the identifier after the keyword `oftype`: + +```jayvee +block GasReserveHttpExtractor oftype HttpExtractor { + // key: value + url: "https://www.bundesnetzagentur.de/_tools/SVG/js2/_functions/csv_export.html?view=renderCSV&id=1089590"; +} +``` + +In the example above, the `url` property of type `text` is defined by the corresponding `HttpExtractor` block type. + +Blocks can be either defined as part of the language, called `builtin` or defined as composition of existing blocks by users in Jayvee, called `composite`. See the documentation for [Composite Blocks](./composite-blocks.md). + +## ValueTypes + +A `ValueType` is the definition of a data type of the processed data. +Some `Blocks` use `ValueTypes` to define logic (like filtering or assessing the data type in a data sink). +We differentiate the following types of `ValueTypes`: +- `Built-in ValueTypes` come with the basic version of Jayvee. See [Built-in Valuetypes](./valuetypes/builtin-valuetypes). +- `Primitive ValueTypes` can be defined by the user to model domain-specific data types and represent a single value. + `Constraints` can be added to a `Primitive ValueType`. +See [Primitive Valuetypes](./valuetypes/primitive-valuetypes). +- `Compound ValueTypes`: UPCOMING. + +```jayvee +valuetype GasFillLevel oftype integer { + constraints: [ GasFillLevelRange ]; +} + +constraint GasFillLevelRange on decimal: + value >= 0 and value <= 100; +``` + +## Transforms +`Transforms` are used to transform data from one `ValueType` to a different one. For more details, see [Transforms](./transforms.md). + +```jayvee +transform CelsiusToKelvin { + from tempCelsius oftype decimal; + to tempKelvin oftype decimal; + + tempKelvin: tempCelsius + 273.15; +} +``` \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.4.0/user/core-concepts.md.license b/apps/docs/versioned_docs/version-0.4.0/user/core-concepts.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/core-concepts.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.4.0/user/examples/README.mdx b/apps/docs/versioned_docs/version-0.4.0/user/examples/README.mdx new file mode 100644 index 00000000..3b8b9448 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/examples/README.mdx @@ -0,0 +1,15 @@ +--- +sidebar_position: 10 +--- + +# Jayvee Examples + +Examples of Jayvee models. +Copy them to your local file system and execute them with the `jv` command on your command line (see [usage](/docs/user/intro#usage)). +You can [find all examples on Github](https://github.com/jvalue/jayvee/tree/main/example). + +```mdx-code-block +import DocCardList from '@theme/DocCardList'; + +<DocCardList /> +``` diff --git a/apps/docs/versioned_docs/version-0.4.0/user/examples/README.mdx.license b/apps/docs/versioned_docs/version-0.4.0/user/examples/README.mdx.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/examples/README.mdx.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.4.0/user/examples/cars.md b/apps/docs/versioned_docs/version-0.4.0/user/examples/cars.md new file mode 100644 index 00000000..ea96dbfa --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/examples/cars.md @@ -0,0 +1,110 @@ +--- +title: cars +--- + +```jayvee +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// Example 1: Cars +// Learning goals: +// - Understand the core concepts pipeline, block, and pipe +// - Understand the general structure of a pipeline + +// 1. This Jayvee model describes a pipeline +// from a CSV file in the web +// to a SQLite file sink. +pipeline CarsPipeline { + + // 2. We describe the structure of the pipeline, + // usually at the top of the pipeline. + // by connecting blocks via pipes. + + // 3. Syntax of a pipe + // connecting the block CarsExtractor + // with the block CarsTextFileInterpreter. + CarsExtractor -> CarsTextFileInterpreter; + + // 4. The output of the preceding block is hereby used + // as input for the succeeding block. + + // 5. Pipes can be further chained, + // leading to an overview of the pipeline. + CarsTextFileInterpreter + -> CarsCSVInterpreter + -> NameHeaderWriter + -> CarsTableInterpreter + -> CarsLoader; + + + // 6. Below the pipes, we usually define the blocks + // that are connected by the pipes. + + // 7. Blocks instantiate a blocktype by using the oftype keyword. + // The blocktype defines the available properties that the block + // can use to specify the intended behavior of the block + block CarsExtractor oftype HttpExtractor { + + // 8. Properties are assigned to concrete values. + // Here, we specify the URL where the file shall be downloaded from. + url: "https://gist.githubusercontent.com/noamross/e5d3e859aa0c794be10b/raw/b999fb4425b54c63cab088c0ce2c0d6ce961a563/cars.csv"; + } + + // 9. The HttpExtractor requires no input and produces a binary file as output. + // This file has to be interpreted, e.g., as text file. + block CarsTextFileInterpreter oftype TextFileInterpreter { } + + // 10. Next, we interpret the text file as sheet. + // A sheet only contains text cells and is useful for manipulating the shape of data before assigning more strict value types to cells. + block CarsCSVInterpreter oftype CSVInterpreter { + enclosing: '"'; + } + + // 11. We can write into cells of a sheet using the CellWriter blocktype. + block NameHeaderWriter oftype CellWriter { + // 12. We utilize a syntax similar to spreadsheet programs. + // Cell ranges can be described using the keywords "cell", "row", "column", or "range" that indicate which + // cells are selected for the write action. + at: cell A1; + + // 13. For each cell we selected with the "at" property above, + // we can specify what value shall be written into the cell. + write: ["name"]; + } + + // 14. As a next step, we interpret the sheet as a table by adding structure. + // We define a valuetype per column that specifies the data type of the column. + // Rows that include values that are not valid according to the their valuetypes are dropped automatically. + block CarsTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "name" oftype text, + "mpg" oftype decimal, + "cyl" oftype integer, + "disp" oftype decimal, + "hp" oftype integer, + "drat" oftype decimal, + "wt" oftype decimal, + "qsec" oftype decimal, + "vs" oftype integer, + "am" oftype integer, + "gear" oftype integer, + "carb" oftype integer + ]; + } + + // 15. As a last step, we load the table into a sink, + // here into a sqlite file. + // The structural information of the table is used + // to generate the correct table. + block CarsLoader oftype SQLiteLoader { + table: "Cars"; + file: "./cars.sqlite"; + } + + // 16. Congratulations! + // You can now use the sink for your data analysis, app, + // or whatever you want to do with the cleaned data. +} +``` \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.4.0/user/examples/cars.md.license b/apps/docs/versioned_docs/version-0.4.0/user/examples/cars.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/examples/cars.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.4.0/user/examples/composite-block.md b/apps/docs/versioned_docs/version-0.4.0/user/examples/composite-block.md new file mode 100644 index 00000000..8151c7c5 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/examples/composite-block.md @@ -0,0 +1,98 @@ +--- +title: composite-block +--- + +```jayvee +composite blocktype TextFileExtractor { + property url oftype text; + + input inputName oftype None; + output outputName oftype File; + + block FileExtractor oftype HttpExtractor { url: url; } + + block FileTextInterpreter oftype TextFileInterpreter {} + + inputName + ->FileExtractor + ->FileTextInterpreter + ->outputName; +} + +composite blocktype CSVExtractor { + property url oftype text; + property delimiter oftype text: ','; + property enclosing oftype text: ''; + property enclosingEscape oftype text: ''; + + input inputName oftype None; + output outputName oftype Sheet; + + block TextFileExtractor oftype TextFileExtractor { url: url; } + + block FileCSVInterpreter oftype CSVInterpreter { + delimiter: delimiter; + enclosing: enclosing; + enclosingEscape: enclosingEscape; + } + + inputName + ->TextFileExtractor + ->FileCSVInterpreter + ->outputName; +} + +composite blocktype CarsTableInterpreterForTesting { + input inputName oftype Sheet; + output outputName oftype Table; + + block CarsTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "name" oftype text, + "mpg" oftype decimal, + "cyl" oftype integer, + "disp" oftype decimal, + "hp" oftype integer, + "drat" oftype decimal, + "wt" oftype decimal, + "qsec" oftype decimal, + "vs" oftype integer, + "am" oftype integer, + "gear" oftype integer, + "carb" oftype integer + ]; + } + + inputName->CarsTableInterpreter->outputName; +} + +composite blocktype SinkTester { + input inputName oftype Table; + output outputName oftype None; + + block CarsLoader oftype SQLiteLoader { + table: "Cars"; + file: "./compositeblockstest.sqlite"; + } + + inputName->CarsLoader->outputName; +} + +pipeline CarsPipeline { + CarsCSVExtractor + -> TestCompositeWithInput + -> CarsLoader; + + block CarsCSVExtractor oftype CSVExtractor { + url: "https://gist.githubusercontent.com/noamross/e5d3e859aa0c794be10b/raw/b999fb4425b54c63cab088c0ce2c0d6ce961a563/cars.csv"; + enclosing: '"'; + } + + block TestCompositeWithInput oftype CarsTableInterpreterForTesting { + } + + block CarsLoader oftype SinkTester { + } +} +``` \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.4.0/user/examples/composite-block.md.license b/apps/docs/versioned_docs/version-0.4.0/user/examples/composite-block.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/examples/composite-block.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.4.0/user/examples/electric-vehicles.md b/apps/docs/versioned_docs/version-0.4.0/user/examples/electric-vehicles.md new file mode 100644 index 00000000..99398633 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/examples/electric-vehicles.md @@ -0,0 +1,150 @@ +--- +title: electric-vehicles +--- + +```jayvee +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// Example 2: Electric Vehicles +// Learning goals: +// - Understand further core concepts transforms and valuetypes +// - Understand how to construct a pipeline with multiple sinks +// - Understand the use of runtime parameters + +// 1. This Jayvee model describes a pipeline +// from a CSV file in the web +// to a SQLite file and a PostgreSQL db sink. +pipeline ElectricVehiclesPipeline { + // See here for meta-data of the data source + // https://catalog.data.gov/dataset/electric-vehicle-population-data/resource/fa51be35-691f-45d2-9f3e-535877965e69 + + // 2. At the top of a pipeline, we describe the + // structure of the pipeline. The first part until + // the ElectricRangeTransformer is a linear sequence + // of blocks. From there we can see a split into two + // parallel sequences that load the data in to two + // different sinks. + ElectricVehiclesHttpExtractor + -> ElectricVehiclesTextFileInterpreter + -> ElectricVehiclesCSVInterpreter + -> ElectricVehiclesTableInterpreter + -> ElectricRangeTransformer; + + ElectricRangeTransformer + -> ElectricVehiclesSQLiteLoader; + + ElectricRangeTransformer + -> ElectricVehiclesPostgresLoader; + + // 3. After the pipeline structure, we define the blocks used. + block ElectricVehiclesHttpExtractor oftype HttpExtractor { + url: "https://data.wa.gov/api/views/f6w7-q2d2/rows.csv?accessType=DOWNLOAD"; + } + + block ElectricVehiclesTextFileInterpreter oftype TextFileInterpreter { } + + block ElectricVehiclesCSVInterpreter oftype CSVInterpreter { } + + block ElectricVehiclesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + // 4. Here, a user-deifned valuetype is used to describe this column. + // The capital letter indicates that the valuetype is not builtin + // by convention. The valuetype itself is defined further below. + "VIN (1-10)" oftype VehicleIdentificationNumber10, + "County" oftype text, + "City" oftype text, + "State" oftype UsStateCode, + "Postal Code" oftype text, + "Model Year" oftype integer, + "Make" oftype text, + "Model" oftype text, + "Electric Vehicle Type" oftype text, + "Clean Alternative Fuel Vehicle (CAFV) Eligibility" oftype text, + "Electric Range" oftype integer, + "Base MSRP" oftype integer, + "Legislative District" oftype text, + "DOL Vehicle ID" oftype integer, + "Vehicle Location" oftype text, + "Electric Utility" oftype text, + "2020 Census Tract" oftype text, + ]; + } + + // 5. This block describes the application of a transform function + // taking a column as input and adding another computed column. + // The applied transform function is defined below and referenced + // by the "use" property. + block ElectricRangeTransformer oftype TableTransformer { + inputColumns: ["Electric Range"]; + outputColumn: "Electric Range (km)"; + use: MilesToKilometers; + } + + // 6. Here, we define a transform function, taking parameters + // as input ("from" keyword), and producing an output ("to" keyword). + // Inputs and outputs have to be further described by a valuetype. + transform MilesToKilometers { + from miles oftype decimal; + to kilometers oftype integer; + + // 7. In order to express what the transform function does, + // we assign an expression to the output. Values from the input and output of the transform can be referred to by name. + kilometers: round (miles * 1.609344); + } + + block ElectricVehiclesSQLiteLoader oftype SQLiteLoader { + table: "ElectricVehiclePopulationData"; + file: "./electric-vehicles.sqlite"; + } + + block ElectricVehiclesPostgresLoader oftype PostgresLoader { + // 8. The requires keyword allows us to define runtime parameters. + // These values have to be provided as environment variables when interpreting the Jayvee model. + host: requires DB_HOST; + port: requires DB_PORT; + username: requires DB_USERNAME; + password: requires DB_PASSWORD; + database: requires DB_DATABASE; + table: "ElectricVehiclePopulationData"; + } +} + +// 9. Below the pipeline, we model user-define valuetypes. +// We give them a speaking name and provide a base valuetype +// that this valuetype builts on. User-defined valuetypes always place additional constraints on existing valuetypes. +valuetype VehicleIdentificationNumber10 oftype text { + // 10. Valuetypes can be further refined by providing constraints. + constraints: [ + OnlyCapitalLettersAndDigits, + ExactlyTenCharacters, + ]; +} + +// 11. This constraint works on text valuetypes and requires values +// to match a given regular expression in order to be valid. +constraint OnlyCapitalLettersAndDigits on text: + value matches /^[A-Z0-9]*$/; + +constraint ExactlyTenCharacters on text: + value.length == 10; + +valuetype UsStateCode oftype text { + constraints: [ + UsStateCodeAllowlist, + ]; +} + +constraint UsStateCodeAllowlist on text: + value in [ + "AL", "AK", "AZ", "AR", "AS", "CA", "CO", "CT", "DE", "DC", + "FL", "GA", "GU", "HI", "ID", "IL", "IN", "IA", "KS", "KY", + "LA", "ME", "MD", "MA", "MI", "MN", "MS", "MO", "MT", "NE", + "NV", "NH", "NJ", "NM", "NY", "NC", "ND", "MP", "OH", "OK", + "OR", "PA", "PR", "RI", "SC", "SD", "TN", "TX", "TT", "UT", + "VT", "VA", "VI", "WA", "WV", "WI", "WY", + ]; + +``` \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.4.0/user/examples/electric-vehicles.md.license b/apps/docs/versioned_docs/version-0.4.0/user/examples/electric-vehicles.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/examples/electric-vehicles.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.4.0/user/examples/gtfs-rt.md b/apps/docs/versioned_docs/version-0.4.0/user/examples/gtfs-rt.md new file mode 100644 index 00000000..7610d8c1 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/examples/gtfs-rt.md @@ -0,0 +1,133 @@ +--- +title: gtfs-rt +--- + +```jayvee +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// Example 3: GTFS Realtime Data +// Learning goals: +// - Understand the construction of a csv file with multiple tables +// - Understand how to work with live data + +// 1. This Jayvee model describes a pipeline +// from a GTFS RT data source in the web +// to a SQLite file with multiple tables. +pipeline GtfsRTSimplePipeline { + + // 2. As you can see here, we have three independent + // sequences of pipes in this pipeline. + GTFSRTTripUpdateFeedExtractor + ->GtfsRTTripUpdateInterpreter + ->TripUpdateTableInterpreter + ->TripUpdateLoader; + + GTFSRTVehiclePositionFeedExtractor + ->GtfsRTVehiclePositionInterpreter + ->VehiclePositionTableInterpreter + ->VehicleLoader; + + GTFSRTAlertFeedExtractor + ->GtfsRTAlertInterpreter + ->AlertTableInterpreter + ->AlertLoader; + + // 3. We define a series of HttpExtractors that each pull data + // from an HTTP endpoint + block GTFSRTTripUpdateFeedExtractor oftype HttpExtractor { + url: "https://proxy.transport.data.gouv.fr/resource/bibus-brest-gtfs-rt-trip-update"; + } + + block GTFSRTVehiclePositionFeedExtractor oftype HttpExtractor { + url: "https://proxy.transport.data.gouv.fr/resource/bibus-brest-gtfs-rt-vehicle-position"; + } + + block GTFSRTAlertFeedExtractor oftype HttpExtractor { + url: "https://proxy.transport.data.gouv.fr/resource/bibus-brest-gtfs-rt-alerts"; + } + + // 4. In the next step, we use the domain-specific GtfsRTInterpreter + // to interpret the fetched files as sheets + block GtfsRTTripUpdateInterpreter oftype GtfsRTInterpreter { + entity: "trip_update"; + } + + block GtfsRTAlertInterpreter oftype GtfsRTInterpreter { + entity: "alert"; + } + + block GtfsRTVehiclePositionInterpreter oftype GtfsRTInterpreter { + entity: "vehicle"; + } + + // 5. Next, we interpret the sheets as tables + block TripUpdateTableInterpreter oftype TableInterpreter { + header: true; + columns:[ + "header.gtfs_realtime_version" oftype text, + "header.timestamp" oftype text, + "header.incrementality" oftype text, + "entity.id" oftype text, + "entity.trip_update.trip.trip_id" oftype text, + "entity.trip_update.trip.route_id" oftype text, + "entity.trip_update.stop_time_update.stop_sequence" oftype text, + "entity.trip_update.stop_time_update.stop_id" oftype text, + "entity.trip_update.stop_time_update.arrival.time" oftype text, + "entity.trip_update.stop_time_update.departure.time" oftype text, + ]; + } + + block VehiclePositionTableInterpreter oftype TableInterpreter { + header: true; + columns:[ + "header.gtfs_realtime_version" oftype text, + "header.timestamp" oftype text, + "header.incrementality" oftype text, + "entity.id" oftype text, + "entity.vehicle_position.vehicle_descriptor.id" oftype text, + "entity.vehicle_position.trip.trip_id" oftype text, + "entity.vehicle_position.trip.route_id" oftype text, + "entity.vehicle_position.position.latitude" oftype text, + "entity.vehicle_position.position.longitude" oftype text, + "entity.vehicle_position.timestamp" oftype text + ]; + } + + block AlertTableInterpreter oftype TableInterpreter { + header: true; + columns:[ + 'header.gtfs_realtime_version' oftype text, + 'header.timestamp' oftype text, + 'header.incrementality' oftype text, + 'entity.id' oftype text, + 'entity.alert.informed_entity.route_id' oftype text, + 'entity.alert.header_text' oftype text, + 'entity.alert.description_text' oftype text, + ]; + } + + // 6. Last, we load the tables into the same SQLite file. + // Each loader has to define a different table name. + // For working with live data, we use the property "dropTable: false" + // to append data instead of deleting the previous data. + block TripUpdateLoader oftype SQLiteLoader { + table: "gtfs-rt-trip_update"; + file: "./gtfs.sqlite"; + dropTable: false; + } + + block VehicleLoader oftype SQLiteLoader { + table: "gtfs-rt-vehicle_position"; + file: "./gtfs.sqlite"; + dropTable: false; + } + + block AlertLoader oftype SQLiteLoader { + table: "gtfs-rt-alert"; + file: "./gtfs.sqlite"; + dropTable: false; + } +} +``` \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.4.0/user/examples/gtfs-rt.md.license b/apps/docs/versioned_docs/version-0.4.0/user/examples/gtfs-rt.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/examples/gtfs-rt.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.4.0/user/examples/gtfs-static.md b/apps/docs/versioned_docs/version-0.4.0/user/examples/gtfs-static.md new file mode 100644 index 00000000..0e24922b --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/examples/gtfs-static.md @@ -0,0 +1,365 @@ +--- +title: gtfs-static +--- + +```jayvee +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// Example 4: GTFS Static Data +// Learning goals: +// - Understand how to work with file systems + +// 1. This Jayvee model describes a pipeline +// from a zip file in the GTFS format in the web +// to a joint SQLite file with multiple tables. +pipeline GtfsPipeline { + + // 2. The origin for multiple pipe sequences is a zip + // file. Each csv file in this zip is further processed + // by its own sequence of blocks and pipes. + + GTFSSampleFeedExtractor + -> AgencyFilePicker + -> AgencyTextFileInterpreter + -> AgencyCSVInterpreter + -> AgencyTableInterpreter + -> AgencyLoader; + + GTFSSampleFeedExtractor + -> CalendarDatesFilePicker + -> CalendarDatesTextFileInterpreter + -> CalendarDatesCSVInterpreter + -> CalendarDatesTableInterpreter + -> CalendarDatesLoader; + + GTFSSampleFeedExtractor + -> CalendarFilePicker + -> CalendarTextFileInterpreter + -> CalendarCSVInterpreter + -> CalendarTableInterpreter + -> CalendarLoader; + + GTFSSampleFeedExtractor + -> FareAttributesFilePicker + -> FareAttributesTextFileInterpreter + -> FareAttributesCSVInterpreter + -> FareAttributesTableInterpreter + -> FareAttributesLoader; + + GTFSSampleFeedExtractor + -> FareRulesFilePicker + -> FareRulesTextFileInterpreter + -> FareRulesCSVInterpreter + -> FareRulesTableInterpreter + -> FareRulesLoader; + + GTFSSampleFeedExtractor + -> FrequenciesFilePicker + -> FrequenciesTextFileInterpreter + -> FrequenciesCSVInterpreter + -> FrequenciesTableInterpreter + -> FrequenciesLoader; + + GTFSSampleFeedExtractor + -> RoutesFilePicker + -> RoutesTextFileInterpreter + -> RoutesCSVInterpreter + -> RoutesTableInterpreter + -> RoutesLoader; + + GTFSSampleFeedExtractor + -> ShapesFilePicker + -> ShapesTextFileInterpreter + -> ShapesCSVInterpreter + -> ShapesTableInterpreter + -> ShapesLoader; + + GTFSSampleFeedExtractor + -> StopTimesFilePicker + -> StopTimesTextFileInterpreter + -> StopTimesCSVInterpreter + -> StopTimesTableInterpreter + -> StopTimesLoader; + + GTFSSampleFeedExtractor + -> StopsFilePicker + -> StopsTextFileInterpreter + -> StopsCSVInterpreter + -> StopsTableInterpreter + -> StopsLoader; + + GTFSSampleFeedExtractor + -> TripsFilePicker + -> TripsTextFileInterpreter + -> TripsCSVInterpreter + -> TripsTableInterpreter + -> TripsLoader; + + // 3. As a first step, we download the zip file and interpret it. + block GTFSSampleFeedExtractor oftype GTFSExtractor { + url: "https://developers.google.com/static/transit/gtfs/examples/sample-feed.zip"; + } + + // 4. Next, we pick several csv files (with the file extension ".txt") + // for further processing . + block AgencyFilePicker oftype FilePicker { + path: "/agency.txt"; + } + + block CalendarDatesFilePicker oftype FilePicker { + path: "/calendar_dates.txt"; + } + + block CalendarFilePicker oftype FilePicker { + path: "/calendar.txt"; + } + + block FareAttributesFilePicker oftype FilePicker { + path: "/fare_attributes.txt"; + } + + block FareRulesFilePicker oftype FilePicker { + path: "/fare_rules.txt"; + } + + block FrequenciesFilePicker oftype FilePicker { + path: "/frequencies.txt"; + } + + block RoutesFilePicker oftype FilePicker { + path: "/routes.txt"; + } + + block ShapesFilePicker oftype FilePicker { + path: "/shapes.txt"; + } + + block StopTimesFilePicker oftype FilePicker { + path: "/stop_times.txt"; + } + + block StopsFilePicker oftype FilePicker { + path: "/stops.txt"; + } + + block TripsFilePicker oftype FilePicker { + path: "/trips.txt"; + } + + // 5. The rest of the pipeline follows the usual pattern. + block AgencyTextFileInterpreter oftype TextFileInterpreter { } + block CalendarDatesTextFileInterpreter oftype TextFileInterpreter { } + block CalendarTextFileInterpreter oftype TextFileInterpreter { } + block FareAttributesTextFileInterpreter oftype TextFileInterpreter { } + block FareRulesTextFileInterpreter oftype TextFileInterpreter { } + block FrequenciesTextFileInterpreter oftype TextFileInterpreter { } + block RoutesTextFileInterpreter oftype TextFileInterpreter { } + block ShapesTextFileInterpreter oftype TextFileInterpreter { } + block StopTimesTextFileInterpreter oftype TextFileInterpreter { } + block StopsTextFileInterpreter oftype TextFileInterpreter { } + block TripsTextFileInterpreter oftype TextFileInterpreter { } + block AgencyCSVInterpreter oftype CSVInterpreter { } + block CalendarDatesCSVInterpreter oftype CSVInterpreter { } + block CalendarCSVInterpreter oftype CSVInterpreter { } + block FareAttributesCSVInterpreter oftype CSVInterpreter { } + block FareRulesCSVInterpreter oftype CSVInterpreter { } + block FrequenciesCSVInterpreter oftype CSVInterpreter { } + block RoutesCSVInterpreter oftype CSVInterpreter { } + block ShapesCSVInterpreter oftype CSVInterpreter { } + block StopTimesCSVInterpreter oftype CSVInterpreter { } + block StopsCSVInterpreter oftype CSVInterpreter { } + block TripsCSVInterpreter oftype CSVInterpreter { } + + block AgencyTableInterpreter oftype TableInterpreter { + header: true; + columns:[ + "agency_id" oftype text, //Conditional columns are considered as required + "agency_name" oftype text, + "agency_url" oftype text, + "agency_timezone" oftype text + ]; + } + + block CalendarDatesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "service_id" oftype text, + "date" oftype text, + "exception_type" oftype text + ]; + } + + block CalendarTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "service_id" oftype text, + "monday" oftype text, + "tuesday" oftype text, + "wednesday" oftype text, + "thursday" oftype text, + "friday" oftype text, + "saturday" oftype text, + "sunday" oftype text, + "start_date" oftype text, + "end_date" oftype text + ]; + } + + block FareAttributesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "fare_id" oftype text, + "price" oftype text, + "currency_type" oftype text, + "payment_method" oftype text, + "transfers" oftype text, + "transfer_duration" oftype text + ]; + } + + block FareRulesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "fare_id" oftype text, + "route_id" oftype text, + "origin_id" oftype text, + "destination_id" oftype text, + "contains_id" oftype text + ]; + } + + block FrequenciesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "trip_id" oftype text, + "start_time" oftype text, + "end_time" oftype text, + "headway_secs" oftype text + ]; + } + + block RoutesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "route_id" oftype text, + "agency_id" oftype text, + "route_short_name" oftype text, + "route_long_name" oftype text, + "route_desc" oftype text, + "route_type" oftype text, + "route_url" oftype text, + "route_color" oftype text, + "route_text_color" oftype text + ]; + } + + block ShapesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "shape_id" oftype text, + "shape_pt_lat" oftype text, + "shape_pt_lon" oftype text, + "shape_pt_sequence" oftype text, + "shape_dist_traveled" oftype text + ]; + } + + block StopTimesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "trip_id" oftype text, + "arrival_time" oftype text, + "departure_time" oftype text, + "stop_id" oftype text, + "stop_sequence" oftype text, + "stop_headsign" oftype text, + "pickup_type" oftype text, + "drop_off_time" oftype text, + "shape_dist_traveled" oftype text + ]; + } + + block StopsTableInterpreter oftype TableInterpreter { + header: true; + columns:[ + "stop_id" oftype text, + "stop_name" oftype text, + "stop_desc" oftype text, + "stop_lat" oftype text, + "stop_lon" oftype text, + "zone_id" oftype text, + "stop_url" oftype text + ]; + } + + block TripsTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "route_id" oftype text, + "service_id" oftype text, + "trip_id" oftype text, + "trip_headsign" oftype text, + "direction_id" oftype text, + "block_id" oftype text, + "shape_id" oftype text + ]; + } + + block AgencyLoader oftype SQLiteLoader { + table: "agency"; + file: "./gtfs.sqlite"; + } + + block CalendarDatesLoader oftype SQLiteLoader { + table: "calendar_dates"; + file: "./gtfs.sqlite"; + } + + block CalendarLoader oftype SQLiteLoader { + table: "calendar"; + file: "./gtfs.sqlite"; + } + + block FareAttributesLoader oftype SQLiteLoader { + table: "fare_attributes"; + file: "./gtfs.sqlite"; + } + + block FareRulesLoader oftype SQLiteLoader { + table: "fare_rules"; + file: "./gtfs.sqlite"; + } + + block FrequenciesLoader oftype SQLiteLoader { + table: "frequencies"; + file: "./gtfs.sqlite"; + } + + block RoutesLoader oftype SQLiteLoader { + table: "routes"; + file: "./gtfs.sqlite"; + } + + block ShapesLoader oftype SQLiteLoader { + table: "shapes"; + file: "./gtfs.sqlite"; + } + + block StopTimesLoader oftype SQLiteLoader { + table: "stop_times"; + file: "./gtfs.sqlite"; + } + + block StopsLoader oftype SQLiteLoader { + table: "stops"; + file: "./gtfs.sqlite"; + } + + block TripsLoader oftype SQLiteLoader { + table: "trips"; + file: "./gtfs.sqlite"; + } +} +``` \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.4.0/user/examples/gtfs-static.md.license b/apps/docs/versioned_docs/version-0.4.0/user/examples/gtfs-static.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/examples/gtfs-static.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.4.0/user/examples/local-test.md b/apps/docs/versioned_docs/version-0.4.0/user/examples/local-test.md new file mode 100644 index 00000000..62d46d22 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/examples/local-test.md @@ -0,0 +1,70 @@ +--- +title: local-test +--- + +```jayvee +pipeline CarsPipeline { + + transform BeeTransform { + from inputName oftype text; + to outputName oftype text; + + outputName: inputName replace /Hornet/ with 'Bee'; + } + + CarsExtractor -> CarsTextFileInterpreter; + + CarsTextFileInterpreter + -> CarsCSVInterpreter + -> NameHeaderWriter + -> CarsTableInterpreter + -> BeeTransformer + -> CarsLoader; + + block CarsExtractor oftype HttpExtractor { + url: "https://gist.githubusercontent.com/noamross/e5d3e859aa0c794be10b/raw/b999fb4425b54c63cab088c0ce2c0d6ce961a563/cars.csv"; + } + + block CarsTextFileInterpreter oftype TextFileInterpreter { } + + block CarsCSVInterpreter oftype CSVInterpreter { + enclosing: '"'; + } + + block NameHeaderWriter oftype CellWriter { + at: cell A1; + + write: ["name"]; + } + + block CarsTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "name" oftype text, + "mpg" oftype decimal, + "cyl" oftype integer, + "disp" oftype decimal, + "hp" oftype integer, + "drat" oftype decimal, + "wt" oftype decimal, + "qsec" oftype decimal, + "vs" oftype integer, + "am" oftype integer, + "gear" oftype integer, + "carb" oftype integer + ]; + } + + + block BeeTransformer oftype TableTransformer { + inputColumns: ['name']; + outputColumn: 'name'; + use: BeeTransform; + } + + block CarsLoader oftype SQLiteLoader { + table: "Cars"; + file: "./cars.sqlite"; + } +} +``` \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.4.0/user/examples/local-test.md.license b/apps/docs/versioned_docs/version-0.4.0/user/examples/local-test.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/examples/local-test.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.4.0/user/examples/workbooks-xlsx.md b/apps/docs/versioned_docs/version-0.4.0/user/examples/workbooks-xlsx.md new file mode 100644 index 00000000..a46e886a --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/examples/workbooks-xlsx.md @@ -0,0 +1,97 @@ +--- +title: workbooks-xlsx +--- + +```jayvee +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// Example 1: LightTrapping +// Learning goals: +// - Understand how to work with XLSX files and workbooks + +// 1. This Jayvee model describes a pipeline +// from a XLSX file with multiple Sheets in the web +// to a SQLite file sink. +pipeline LightTrappingSiliconSolarCellsPipeline { + // 2. We directly get the xlsx file from the web via the HttpExtractor + // The data is provided under CC BY-SA 4.0 + // Saive, Rebecca (2023). Data supporting the publication: + // Light trapping in thin silicon solar cells: a review on fundamentals and technologies. + // 4TU.ResearchData. Dataset. https://doi.org/10.4121/14554815.v1 + block LightTrappingSiliconSolarCellsExtractor oftype HttpExtractor { + url: "https://figshare.com/ndownloader/files/27923598"; + } + + // 3. The incoming file is interpreted as a XLSX file and transformed into a Workbook + // Workbooks contain at least 1 Sheet. Every sheet has a unique name. + block LightTrappingSiliconSolarCellsTextXLSXInterpreter oftype XLSXInterpreter { + + } + + // 4.1 Here, we pick one sheet with the name 'RefractiveIndexSi GaAs' from the Workbook to use within our pipeline. + // The output type from SheetPicker is Sheet, which was already introduced in the cars example + block LightTrappingSiliconSolarCellsSheetpicker oftype SheetPicker { + sheetName: 'RefractiveIndexSi GaAs'; + } + + block NameHeaderWriter oftype CellWriter { + at: range F1:L1; + write: ["F","G","nm","wl","n2", "k2", "alpha (cm-1)2"]; + } + + block LightTrappingSiliconSolarCellsTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "Wavelength" oftype integer, + "Wavelength (µm)" oftype decimal, + "n" oftype decimal, + "k" oftype text, + "alpha (cm-1)" oftype text, + "nm" oftype decimal, + "n2" oftype text, + "k2" oftype decimal, + "alpha (cm-1)2" oftype decimal + ]; + } + + block LightTrappingSiliconSolarCellsLoader oftype SQLiteLoader { + table: "LightTrappingSiliconSolarCells"; + file: "./LightTrappingSiliconSolarCells.sqlite"; + } + + // 4.2 Here, we pick another sheet named 'Wavelength thickness trapping' from the Workbook + block SecondLightTrappingSiliconSolarCellsSheetpicker oftype SheetPicker { + sheetName: 'Wavelength thickness trapping'; + } + + block SecondLightTrappingSiliconSolarCellsTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "n" oftype decimal, + "Wavelength (µm)" oftype decimal, + ]; + } + + block SecondLightTrappingSiliconSolarCellsLoader oftype SQLiteLoader { + + table: "SecondLightTrappingSiliconSolarCells"; + file: "./LightTrappingSiliconSolarCells.sqlite"; + } + + LightTrappingSiliconSolarCellsExtractor + -> LightTrappingSiliconSolarCellsTextXLSXInterpreter + -> LightTrappingSiliconSolarCellsSheetpicker + -> NameHeaderWriter + -> LightTrappingSiliconSolarCellsTableInterpreter + -> LightTrappingSiliconSolarCellsLoader; + + // 5. Once the XLSX file is interpreted, we can split the pipeline and + // work separately on the different sheets from our input file + LightTrappingSiliconSolarCellsTextXLSXInterpreter + -> SecondLightTrappingSiliconSolarCellsSheetpicker + -> SecondLightTrappingSiliconSolarCellsTableInterpreter + -> SecondLightTrappingSiliconSolarCellsLoader; +} +``` \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.4.0/user/examples/workbooks-xlsx.md.license b/apps/docs/versioned_docs/version-0.4.0/user/examples/workbooks-xlsx.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/examples/workbooks-xlsx.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.4.0/user/expressions.md b/apps/docs/versioned_docs/version-0.4.0/user/expressions.md new file mode 100644 index 00000000..3f553d4d --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/expressions.md @@ -0,0 +1,83 @@ +--- +sidebar_position: 7 +--- + +# Expressions + +Expressions in Jayvee are arbitrarily nested statements. They consist of: +- literals (e.g., numbers `5` or strings `"Example"`) +- variables (e.g., declared by `from` properties in [Transforms](./transforms.md)) +- operators (e.g., `*` or `sqrt`) + +Expressions get evaluated at runtime by the interpreter to a [Built-in ValueType](./valuetypes/builtin-valuetypes). + +### Example + +The following expression is evaluated to the `integer` `10`: `(2 + 3) * 2` + +The following expression is evaluated to the `boolean` `true`: `"Example" == "Example"` + +The following expression is evaluated to the `text` `I love Datypus`: `"I love platypuses" replace /platypuses/ with "Datypus"` + +### List of Operators + +#### Arithmetics (binary operators) +- `+` for addition, e.g., `5 + 3` evaluates to `8` +- `-` for subtraction, e.g., `5 - 3` evaluates to `2` +- `*` for multiplication, e.g., `5 * 3` evaluates to `15` +- `/` for division, e.g., `6 / 3` evaluates to `2` +- `%` for modulo, e.g., `5 % 3` evaluates to `2` +- `pow` for power, e.g., `2 pow 3` evaluates to `8` +- `root` for root, e.g., `27 root 3` evaluates to `3` + +#### Arithmetics (unary operators) +- `+` for positive signing, e.g., `+5` evaluates to `5` +- `-` for negative signing, e.g., `-5` evaluates to `-5` +- `sqrt` for square root, e.g., `sqrt 9` evaluates to `3` +- `foor` for flooring a number, e.g., `floor 5.3` evaluates to `5` +- `ceil` for ceiling a number, e.g., `floor 5.3` evaluates to `6` +- `round` for rounding a number, e.g., `floor 5.3` evaluates to `5` + +#### Relational (binary operators) +- `<` for smaller, e.g., `3 < 3` evaluates to `false` +- `<=` for smaller or equal, e.g., `3 <= 3` evaluates to `true` +- `>` for greater, e.g., `3 > 3` evaluates to `false` +- `>=` for greater or equal, e.g., `3 >= 3` evaluates to `true` +- `==` for equal, e.g., `3 == 3` evaluates to `true` +- `!=` for not equal, e.g., `3 != 3` evaluates to `false` + +#### Logical (binary operators) +- `and` for a logical and (both need to be true to evaluate to true) +- `or` for a logical or (at least left or right needs to be true to evaluate to true) +- `xor` for a logical xor (either left or right needs to be true to evaluate to true) + +#### Logical (unary operators) +- `not` for logical negation, `not true` evaluates to `false` + +#### Others (binary operators) +- `matches` for a regex match, e.g., `"A07" matches /^[A-Z0-9]*$/` evaluates to `true` +- `in` for inclusion in an array, e.g., `"a" in ["a", "b", "c"]` evaluates to `true` + +#### Text manipulation (ternary operators) +- `replace [...] with [...]` replaces regex matches in a text with a string + +### Operator Details + +#### `in` Operator + +The `in` operator checks whether a value is included in a collection of values. For example: + +```jayvee +4.5 in [3, 6.5] // evaluates to false +3 in [3.0, 6.5] // evaluates to true +"a" in ["a", "b", "c"] // evaluates to true +``` + +The operator supports `text`, `integer` and `decimal` values as operands. The compatibility of left and right operand types follows these rules: +- For the `in` operator we have a type for the needle (left operand) and a type for the elements in the haystack (right operand). +- There is an automated type conversion as long as it is lossless and clearly defined (integer to decimal as of now). +- We allow any combination of operands that has either: (i) An automated type conversion from needle type (left operand) to the type of the elements in the haystack (right operand), or (ii) the other way around. + + +### Further reading +For a deeper documentation of how expressions and operators work internally, refer to the [developer docs](../dev/04-guides/04-expressions-and-operators.md). \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.4.0/user/expressions.md.license b/apps/docs/versioned_docs/version-0.4.0/user/expressions.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/expressions.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.4.0/user/intro/_category_.json b/apps/docs/versioned_docs/version-0.4.0/user/intro/_category_.json new file mode 100644 index 00000000..9da536e2 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/intro/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Introduction to Jayvee", + "position": 1, + "link": { + "type": "generated-index", + "description": "All the essential information to get started with Jayvee." + } +} diff --git a/apps/docs/versioned_docs/version-0.4.0/user/intro/_category_.json.license b/apps/docs/versioned_docs/version-0.4.0/user/intro/_category_.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/intro/_category_.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.4.0/user/intro/intro.md b/apps/docs/versioned_docs/version-0.4.0/user/intro/intro.md new file mode 100644 index 00000000..c2b4bc05 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/intro/intro.md @@ -0,0 +1,96 @@ +--- +sidebar_position: 1 +title: Getting Started +--- + +# Introduction to Jayvee + +Jayvee is a domain-specific language (DSL) for automated processing of data pipelines. +The Jayvee interpreter allows executing such data pipelines on local machines. +Data engineers can use Jayvee and its interpreter to clean and preprocess data for later activities like data science or machine learning. + +## Installation + +Install the interpreter via `npm`. You will need a **nodejs version >= 17.0.0**. + +```bash +npm install -g @jvalue/jayvee-interpreter +``` + +You can install a specific version using the `@`-syntax, e.g., version `0.0.17`: + +```bash +npm install -g @jvalue/jayvee-interpreter@0.0.17 +``` + +## Update + +Details about how to update Jayvee and the VSCode extension can be found [here](./update.md). + +## Usage + +### Show help + +```console +jv -h +``` + +### Run a `.jv` file + +```console +jv <file> +``` + +Run with **additional debug output**: + +```console +jv <file> -d +``` + +With **runtime parameters**: + +```console +jv -e <param>=<value> -e <param>=<value> ... <file> +``` + +### Debug a `.jv` file + +Print debugging is further configured by the parameters `--debug-granularity` and `--debug-target`. + +```console +jv <file> -d -dg peek +``` + +The value of the parameter `--debug-granularity` (short `-dg`) can have the following values: + +- `peek` to log a short summary, including a small subset of data +- `exhaustive` to log a summary, including the full data +- `minimal` to log a summary, including no additional data (default). + To see logs, debugging has to be enabled using the `-d` flag. + +```console +jv <file> -d --debug-granularity peek +``` + +The parameter `--debug-target` (short `-dt`) allows to specify which blocks should be logged for debugging. Separate block names by comma if multiple blocks are targeted. All blocks are logged if the parameter is omitted. + +```console +jv <file> -d --debug-granularity peek --debug-target MyExtractorBlock,MySinkBlock +``` + +## Examples + +You can find multiple examples with inline explanations [here](../examples/README.mdx). You can copy them to your local file system and execute them with the `jv` command on your command line (see [usage](#usage)). + +## VSCode Plugin + +To set up Jayvee locally in VS Code, you need to install the latest Jayvee VS Code extension. +To install the most recent extension, go to our [latest release](https://github.com/jvalue/jayvee/releases/latest) +and download the `jayvee.vsix` file from the release assets. +Next, go to [this page](https://code.visualstudio.com/docs/editor/extension-marketplace#_install-from-a-vsix) and +follow the instructions for installing the downloaded extension. + +## Troubleshooting + +1. Error `structuredClone is not defined` + - Please make sure you use node version 17+. diff --git a/apps/docs/versioned_docs/version-0.4.0/user/intro/intro.md.license b/apps/docs/versioned_docs/version-0.4.0/user/intro/intro.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/intro/intro.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.4.0/user/intro/update.md b/apps/docs/versioned_docs/version-0.4.0/user/intro/update.md new file mode 100644 index 00000000..3dd746c9 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/intro/update.md @@ -0,0 +1,52 @@ +--- +sidebar_position: 2 +title: Update Jayvee +--- + +# How to update Jayvee + +Jayvee is consistently getting updates. To ensure you use the most recent version, please regularly update the interpreter and VSCode extension. + +### Update Jayvee + Extension + +To update Jayvee, you need to: + +1. Update the interpreter by reinstalling it using npm: + +```bash +npm install -g @jvalue/jayvee-interpreter +``` + +Note: Updating to a specific version works using the `@`-syntax, e.g., version `0.0.17`: + +```bash +npm install -g @jvalue/jayvee-interpreter@0.0.17 +``` + +2. Update your VSCode Extension: + +- Go [here](https://github.com/jvalue/jayvee/releases/latest) to find the latest(or a specific) version of the VSCode Extenstion. + +- Then, download the latest `jayvee.vsix` file. + +- Finally, to install the extension using the CLI, paste the code below into your command line: + +```bash +code --install-extension jayvee.vsix +``` + +If you'd rather use the manual installation, follow this [link](https://code.visualstudio.com/docs/editor/extension-marketplace#_install-from-a-vsix) for the official VSCode documentation. + +### Version Check + +To verify wether the wanted version of Jayvee and VSCode extension where installed successfully, you can run in your command line: + +For **Jayvee**: + +```bash +jv -V +``` + +For the **VSCode extension**: + +Go to the extensions menu, and look for `Jayvee`. The version is then displayed on the information page of the extension. diff --git a/apps/docs/versioned_docs/version-0.4.0/user/intro/update.md.license b/apps/docs/versioned_docs/version-0.4.0/user/intro/update.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/intro/update.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.4.0/user/runtime-parameters.md b/apps/docs/versioned_docs/version-0.4.0/user/runtime-parameters.md new file mode 100644 index 00000000..bbdab1ed --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/runtime-parameters.md @@ -0,0 +1,27 @@ +--- +sidebar_position: 9 +--- + +# Runtime Parameters + +Property values in Jayvee can be assigned to `values` or left open for later configuration via `runtime parameters`. + +## Syntax + +Runtime parameters are indicated by the `requires` keyword, followed by the identifier of the parameter. Example + +```jayvee +block CarsLoader oftype SQLiteLoader { + table: "Cars"; + file: requires CARS_SQLITE_FILE; +} +``` + +## CLI + +The interpreter CLI has to define all existing runtime parameters for execution. +Use the CLI flag `-e` to to define them as key-value pairs of identifier and value. + +```console +jv -e <param>=<value> -e <param>=<value> ... <file> +``` diff --git a/apps/docs/versioned_docs/version-0.4.0/user/runtime-parameters.md.license b/apps/docs/versioned_docs/version-0.4.0/user/runtime-parameters.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/runtime-parameters.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.4.0/user/transforms.md b/apps/docs/versioned_docs/version-0.4.0/user/transforms.md new file mode 100644 index 00000000..bf3dfd7d --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/transforms.md @@ -0,0 +1,77 @@ +--- +sidebar_position: 8 +--- + +# Transforms + +Transforms are a concept in Jayvee to define the transformation of individual values. +They are similar to functions in programming languages, i.e. they perform computations on some input values and produce output values. Transform work by mapping input values to outputs using [expressions](./expressions.md). + +:::info Important + +Up to version `0.0.16`, we only supported a single input for transformers! + +::: + +:::info Important + +In its current state, Jayvee only supports a arbitrary numbers of inputs and a single output for transforms. +For the future, it is planned to support arbitrary numbers for outputs as well. + +::: + + +## Syntax + +The general syntax of transforms looks like this: + +```jayvee +transform <name> { + from <inputName> oftype <inputValuetype>; + to <outputName> oftype <outputValuetype>; + + <outputName>: <expression>; +} +``` + +The `transform` keyword is used to define a transform and give it a name. +The curly braces denote the body of the transform. + +The body first contains the definitions of input and output ports. +Input ports are defined using the `from` keyword whereas output ports use the `to` keyword. +Next, they are given a name and, after the `oftype` keyword, typed with a valuetype. + +Below, there needs to be an output assignment for each output port. +The output assignment defines how a particular output value is computed. +They consist of the name of an output port, followed by a `:`. +Next, an [expression](./expressions.md) specifies how the output value shall be computed. +Names of input ports can be used in such an expression to refer to input values. + +### Example + +The following transform converts temperature values from degree Celsius to Kelvin: + +```jayvee +transform CelsiusToKelvin { + from tempCelsius oftype decimal; + to tempKelvin oftype decimal; + + tempKelvin: tempCelsius + 273.15; +} +``` + +The following transform converts a text based status into a boolean value, `true` if the text is `Active`, `false` for any other value: + +```jayvee +transform StatusToBoolean { + from statusText oftype text; + to statusBoolean oftype boolean; + + statusBoolean: statusText == "Active"; +} +``` + +## Applying transforms to table columns + +Transforms can be applied to columns of a table. +Please refer to the documentation of the [`TableTransformer` block type](./block-types/TableTransformer.md) to find out how. diff --git a/apps/docs/versioned_docs/version-0.4.0/user/transforms.md.license b/apps/docs/versioned_docs/version-0.4.0/user/transforms.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/transforms.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.4.0/user/valuetypes/_category_.json b/apps/docs/versioned_docs/version-0.4.0/user/valuetypes/_category_.json new file mode 100644 index 00000000..ed1ce95f --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/valuetypes/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Valuetypes", + "position": 5, + "link": { + "type": "generated-index", + "description": "Jayvee supports these different kinds of valuetypes." + } +} diff --git a/apps/docs/versioned_docs/version-0.4.0/user/valuetypes/_category_.json.license b/apps/docs/versioned_docs/version-0.4.0/user/valuetypes/_category_.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/valuetypes/_category_.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_docs/version-0.4.0/user/valuetypes/builtin-valuetypes.md b/apps/docs/versioned_docs/version-0.4.0/user/valuetypes/builtin-valuetypes.md new file mode 100644 index 00000000..0bc36dcb --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/valuetypes/builtin-valuetypes.md @@ -0,0 +1,98 @@ +--- +title: Built-in Valuetypes +--- + +<!-- Do NOT change this document as it is auto-generated from the language server --> + +# Description + +For an introduction to valuetypes, see the [Core Concepts](../core-concepts). +Built-in valuetypes come with the basic version of Jayvee. +They are the basis for more restricted [Primitive Valuetypes](./primitive-valuetypes) +that fullfil [Constraints](./primitive-valuetypes#constraints). + +# Available built-in valuetypes + +## Boolean + +### Description + +A boolean value. +Examples: true, false + +### Example 1 + +```jayvee +block ExampleTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "columnName" oftype boolean + ]; +} +``` + +A block of type `TableInterpreter` that + interprets data in the column `columnName` as `boolean`. + +## Decimal + +### Description + +A decimal value. +Example: 3.14 + +### Example 1 + +```jayvee +block ExampleTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "columnName" oftype decimal + ]; +} +``` + +A block of type `TableInterpreter` that + interprets data in the column `columnName` as `decimal`. + +## Integer + +### Description + +An integer value. +Example: 3 + +### Example 1 + +```jayvee +block ExampleTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "columnName" oftype integer + ]; +} +``` + +A block of type `TableInterpreter` that + interprets data in the column `columnName` as `integer`. + +## Text + +### Description + +A text value. +Example: "Hello World" + +### Example 1 + +```jayvee +block ExampleTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "columnName" oftype text + ]; +} +``` + +A block of type `TableInterpreter` that + interprets data in the column `columnName` as `text`. diff --git a/apps/docs/versioned_docs/version-0.4.0/user/valuetypes/builtin-valuetypes.md.license b/apps/docs/versioned_docs/version-0.4.0/user/valuetypes/builtin-valuetypes.md.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/valuetypes/builtin-valuetypes.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.4.0/user/valuetypes/primitive-valuetypes.md b/apps/docs/versioned_docs/version-0.4.0/user/valuetypes/primitive-valuetypes.md new file mode 100644 index 00000000..92400a53 --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/valuetypes/primitive-valuetypes.md @@ -0,0 +1,48 @@ +--- +sidebar_position: 2 +--- +# Primitive ValueTypes + +`Primitive ValueTypes` are based on `Built-in ValueTypes` and use a collection of constraints to restrict the range of valid values. +Such constraints are implicitly connected via a logical `AND` relation. +Note that the `Constraints` need to be applicable to the base-type of the `ValueType` - indicated by the identifier after the keyword `oftype`: + +```jayvee +valuetype GasFillLevel oftype integer { + constraints: [ GasFillLevelRange ]; +} +``` + + +## Constraints + +`Constraints` for `ValueTypes` declare the validity criteria that each concrete value is checked against. + +### Syntax 1: Expression syntax + +The syntax of expression-based `Constraints` uses an expression that evaluates to `true` or `false` for the given `value`. The type of the values the expression is working in is indicated ofter the keyword `on`: + +```jayvee +constraint GasFillLevelRange on decimal: + value >= 0 and value <= 100; +``` + +Refer to the [Expression documentation](../expressions.md) for further reading on expressions. + + +### Syntax 2: Block-like syntax + +The syntax of `Constraints` is similar to the syntax of `Blocks`. +The availability of property keys and their respective `ValueTypes` is determined by the type of the `Constraint` - indicated by the identifier after the keyword `oftype`: + +```jayvee +constraint GasFillLevelRange oftype RangeConstraint { + lowerBound: 0; + lowerBoundInclusive: true; + upperBound: 100; + upperBoundInclusive: true; +} +``` + +Note that the type of `Constraint` also determines its applicability to `ValueTypes`. +For instance, a `RangeConstraint` can only be applied to the numerical types `integer` and `decimal`. \ No newline at end of file diff --git a/apps/docs/versioned_docs/version-0.4.0/user/valuetypes/primitive-valuetypes.md.license b/apps/docs/versioned_docs/version-0.4.0/user/valuetypes/primitive-valuetypes.md.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versioned_docs/version-0.4.0/user/valuetypes/primitive-valuetypes.md.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/docs/versioned_sidebars/version-0.0.16-sidebars.json b/apps/docs/versioned_sidebars/version-0.0.16-sidebars.json new file mode 100644 index 00000000..217726ac --- /dev/null +++ b/apps/docs/versioned_sidebars/version-0.0.16-sidebars.json @@ -0,0 +1,14 @@ +{ + "userDocsSidebar": [ + { + "type": "autogenerated", + "dirName": "user" + } + ], + "devDocsSidebar": [ + { + "type": "autogenerated", + "dirName": "dev" + } + ] +} diff --git a/apps/docs/versioned_sidebars/version-0.0.16-sidebars.json.license b/apps/docs/versioned_sidebars/version-0.0.16-sidebars.json.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_sidebars/version-0.0.16-sidebars.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_sidebars/version-0.0.17-sidebars.json b/apps/docs/versioned_sidebars/version-0.0.17-sidebars.json new file mode 100644 index 00000000..217726ac --- /dev/null +++ b/apps/docs/versioned_sidebars/version-0.0.17-sidebars.json @@ -0,0 +1,14 @@ +{ + "userDocsSidebar": [ + { + "type": "autogenerated", + "dirName": "user" + } + ], + "devDocsSidebar": [ + { + "type": "autogenerated", + "dirName": "dev" + } + ] +} diff --git a/apps/docs/versioned_sidebars/version-0.0.17-sidebars.json.license b/apps/docs/versioned_sidebars/version-0.0.17-sidebars.json.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_sidebars/version-0.0.17-sidebars.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_sidebars/version-0.0.18-sidebars.json b/apps/docs/versioned_sidebars/version-0.0.18-sidebars.json new file mode 100644 index 00000000..217726ac --- /dev/null +++ b/apps/docs/versioned_sidebars/version-0.0.18-sidebars.json @@ -0,0 +1,14 @@ +{ + "userDocsSidebar": [ + { + "type": "autogenerated", + "dirName": "user" + } + ], + "devDocsSidebar": [ + { + "type": "autogenerated", + "dirName": "dev" + } + ] +} diff --git a/apps/docs/versioned_sidebars/version-0.0.18-sidebars.json.license b/apps/docs/versioned_sidebars/version-0.0.18-sidebars.json.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_sidebars/version-0.0.18-sidebars.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_sidebars/version-0.1.0-sidebars.json b/apps/docs/versioned_sidebars/version-0.1.0-sidebars.json new file mode 100644 index 00000000..217726ac --- /dev/null +++ b/apps/docs/versioned_sidebars/version-0.1.0-sidebars.json @@ -0,0 +1,14 @@ +{ + "userDocsSidebar": [ + { + "type": "autogenerated", + "dirName": "user" + } + ], + "devDocsSidebar": [ + { + "type": "autogenerated", + "dirName": "dev" + } + ] +} diff --git a/apps/docs/versioned_sidebars/version-0.1.0-sidebars.json.license b/apps/docs/versioned_sidebars/version-0.1.0-sidebars.json.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_sidebars/version-0.1.0-sidebars.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_sidebars/version-0.2.0-sidebars.json b/apps/docs/versioned_sidebars/version-0.2.0-sidebars.json new file mode 100644 index 00000000..217726ac --- /dev/null +++ b/apps/docs/versioned_sidebars/version-0.2.0-sidebars.json @@ -0,0 +1,14 @@ +{ + "userDocsSidebar": [ + { + "type": "autogenerated", + "dirName": "user" + } + ], + "devDocsSidebar": [ + { + "type": "autogenerated", + "dirName": "dev" + } + ] +} diff --git a/apps/docs/versioned_sidebars/version-0.2.0-sidebars.json.license b/apps/docs/versioned_sidebars/version-0.2.0-sidebars.json.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_sidebars/version-0.2.0-sidebars.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_sidebars/version-0.3.0-sidebars.json b/apps/docs/versioned_sidebars/version-0.3.0-sidebars.json new file mode 100644 index 00000000..217726ac --- /dev/null +++ b/apps/docs/versioned_sidebars/version-0.3.0-sidebars.json @@ -0,0 +1,14 @@ +{ + "userDocsSidebar": [ + { + "type": "autogenerated", + "dirName": "user" + } + ], + "devDocsSidebar": [ + { + "type": "autogenerated", + "dirName": "dev" + } + ] +} diff --git a/apps/docs/versioned_sidebars/version-0.3.0-sidebars.json.license b/apps/docs/versioned_sidebars/version-0.3.0-sidebars.json.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_sidebars/version-0.3.0-sidebars.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versioned_sidebars/version-0.4.0-sidebars.json b/apps/docs/versioned_sidebars/version-0.4.0-sidebars.json new file mode 100644 index 00000000..217726ac --- /dev/null +++ b/apps/docs/versioned_sidebars/version-0.4.0-sidebars.json @@ -0,0 +1,14 @@ +{ + "userDocsSidebar": [ + { + "type": "autogenerated", + "dirName": "user" + } + ], + "devDocsSidebar": [ + { + "type": "autogenerated", + "dirName": "dev" + } + ] +} diff --git a/apps/docs/versioned_sidebars/version-0.4.0-sidebars.json.license b/apps/docs/versioned_sidebars/version-0.4.0-sidebars.json.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/apps/docs/versioned_sidebars/version-0.4.0-sidebars.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/apps/docs/versions.json b/apps/docs/versions.json new file mode 100644 index 00000000..a60408e8 --- /dev/null +++ b/apps/docs/versions.json @@ -0,0 +1,9 @@ +[ + "0.4.0", + "0.3.0", + "0.2.0", + "0.1.0", + "0.0.18", + "0.0.17", + "0.0.16" +] diff --git a/apps/docs/versions.json.license b/apps/docs/versions.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/docs/versions.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/interpreter/.eslintrc.json b/apps/interpreter/.eslintrc.json new file mode 100644 index 00000000..42abee8e --- /dev/null +++ b/apps/interpreter/.eslintrc.json @@ -0,0 +1,21 @@ +{ + "extends": ["../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "parserOptions": { + "project": ["apps/interpreter/tsconfig.app.json", "apps/interpreter/tsconfig.spec.json"] + }, + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/apps/interpreter/.eslintrc.json.license b/apps/interpreter/.eslintrc.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/interpreter/.eslintrc.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/interpreter/README.md b/apps/interpreter/README.md new file mode 100644 index 00000000..0571741e --- /dev/null +++ b/apps/interpreter/README.md @@ -0,0 +1,27 @@ +<!-- +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only +--> + +# Interpreter + +## Run the interpreter locally + +Build the interpreter: + +```console +npx nx run interpreter:build +``` + +See [Usage](#usage) for how to use the interpreter. Replace `jv` with `node dist/apps/interpreter/main.js` when running the commands. + +## Global installation + +See the [official docs](https://jvalue.github.io/jayvee). + + +## Important project files + +- `package.json` - used for publishing the interpreter as npm package. +- `src/index.ts` - the entry point of the command line interface (CLI). diff --git a/apps/interpreter/package.json b/apps/interpreter/package.json new file mode 100644 index 00000000..d080675d --- /dev/null +++ b/apps/interpreter/package.json @@ -0,0 +1,22 @@ +{ + "name": "@jvalue/jayvee-interpreter", + "description": "Interpreter for .jv files", + "main": "main.js", + "bin": { + "jv": "main.js", + "jayvee": "main.js" + }, + "bugs": { + "url": "https://github.com/jvalue/jayvee/issues" + }, + "repository": { + "type": "git", + "url": "https://github.com/jvalue/jayvee.git" + }, + "homepage": "https://github.com/jvalue/jayvee", + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org" + }, + "version": "$REPLACE_WITH_PROGRAM_VERSION_IN_PREPUBLISH_STEP" +} diff --git a/apps/interpreter/package.json.license b/apps/interpreter/package.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/interpreter/package.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/interpreter/project.json b/apps/interpreter/project.json new file mode 100644 index 00000000..6733eb1e --- /dev/null +++ b/apps/interpreter/project.json @@ -0,0 +1,73 @@ +{ + "name": "interpreter", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "apps/interpreter/src", + "projectType": "application", + "targets": { + "build": { + "options": { + "tsConfig": "{projectRoot}/tsconfig.app.json" + } + }, + "lint": { + "executor": "@nx/eslint:lint", + "outputs": ["{options.outputFile}"] + }, + "run": { + "executor": "nx:run-commands", + "dependsOn": ["build"], + "options": { + "commands": ["node --enable-source-maps dist/apps/interpreter/main.js"], + "parallel": false + } + }, + "test": { + "executor": "@nx/vite:test", + "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], + "options": { + "configFile": "{projectRoot}/vite.config.ts", + "passWithNoTests": false + } + }, + "pre-publish": { + "executor": "nx:run-commands", + "dependsOn": ["build"], + "options": { + "commands": [ + "node tools/scripts/interpreter/prepend-shebang.mjs interpreter main.js", + "node tools/scripts/add-package-json-version.mjs interpreter", + "node tools/scripts/interpreter/rewrite-version-mainjs.mjs interpreter", + "node tools/scripts/publish.mjs interpreter false" + ], + "parallel": false + } + }, + "publish": { + "executor": "nx:run-commands", + "dependsOn": ["pre-publish"], + "options": { + "commands": ["node tools/scripts/publish.mjs interpreter true"], + "parallel": false + } + }, + "pack": { + "executor": "nx:run-commands", + "dependsOn": ["pre-publish"], + "options": { + "commands": ["node tools/scripts/pack.mjs interpreter"], + "parallel": false + } + }, + "install": { + "executor": "nx:run-commands", + "dependsOn": ["pack"], + "options": { + "commands": [ + "npm i -g dist/apps/interpreter/jvalue-jayvee-interpreter-*.tgz" + ], + "parallel": false + } + } + }, + "tags": [] +} diff --git a/apps/interpreter/project.json.license b/apps/interpreter/project.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/interpreter/project.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/interpreter/src/examples-smoke-test.spec.ts b/apps/interpreter/src/examples-smoke-test.spec.ts new file mode 100644 index 00000000..f4dfc2a6 --- /dev/null +++ b/apps/interpreter/src/examples-smoke-test.spec.ts @@ -0,0 +1,232 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import path from 'node:path'; + +import { processExitMockImplementation } from '@jvalue/jayvee-execution/test'; +import { + PostgresLoaderExecutorMock, + SQLiteLoaderExecutorMock, +} from '@jvalue/jayvee-extensions/rdbms/test'; +import { HttpExtractorExecutorMock } from '@jvalue/jayvee-extensions/std/test'; +import { + createJayveeServices, + initializeWorkspace, +} from '@jvalue/jayvee-language-server'; +import { NodeFileSystem } from 'langium/node'; +import nock from 'nock'; +import { type MockInstance, vi } from 'vitest'; + +import { runAction } from './run-action'; + +// Mock global imports +vi.mock('pg', () => { + const mClient = { + connect: vi.fn(), + query: vi.fn(), + end: vi.fn(), + }; + return { + default: { + Client: vi.fn(() => mClient), + }, + }; +}); +vi.mock('sqlite3', () => { + const mockDB = { + close: vi.fn(), + run: vi.fn(), + }; + return { + default: { Database: vi.fn(() => mockDB) }, + }; +}); + +describe('jv example smoke tests', () => { + const baseDir = path.resolve(__dirname, '../../../example/'); + + const defaultOptions = { + env: new Map<string, string>(), + debug: false, + debugGranularity: 'minimal', + debugTarget: undefined, + }; + + let exitSpy: MockInstance; + let httpExtractorMock: HttpExtractorExecutorMock; + let postgresLoaderMock: PostgresLoaderExecutorMock; + let sqliteLoaderMock: SQLiteLoaderExecutorMock; + + beforeAll(async () => { + const services = createJayveeServices(NodeFileSystem).Jayvee; + await initializeWorkspace(services); + + exitSpy = vi + .spyOn(process, 'exit') + .mockImplementation(processExitMockImplementation); + httpExtractorMock = new HttpExtractorExecutorMock(); + postgresLoaderMock = new PostgresLoaderExecutorMock(); + sqliteLoaderMock = new SQLiteLoaderExecutorMock(); + }); + + afterEach(() => { + exitSpy.mockClear(); + httpExtractorMock.restore(); + postgresLoaderMock.restore(); + sqliteLoaderMock.restore(); + }); + + it('should have no errors when executing cars.jv example', async () => { + // Prepare mocks + httpExtractorMock.setup(() => { + return [ + nock('https://gist.githubusercontent.com') + .get( + '/noamross/e5d3e859aa0c794be10b/raw/b999fb4425b54c63cab088c0ce2c0d6ce961a563/cars.csv', + ) + .replyWithFile( + 200, + path.resolve(__dirname, '../test/assets/cars.csv'), + { + 'Content-Type': 'text/csv', + }, + ), + ]; + }); + sqliteLoaderMock.setup(); + + await runAction(path.resolve(baseDir, 'cars.jv'), { + ...defaultOptions, + }); + + expect(httpExtractorMock.nockScopes.every((scope) => scope.isDone())); + expect(sqliteLoaderMock.sqliteClient.run).toBeCalledTimes(3); + expect(sqliteLoaderMock.sqliteClient.close).toBeCalledTimes(1); + + expect(exitSpy).toHaveBeenCalledWith(0); + }); + + it('should have no errors when executing electric-vehicles.jv example', async () => { + // Prepare mocks + httpExtractorMock.setup(() => { + return [ + nock('https://data.wa.gov') + .get('/api/views/f6w7-q2d2/rows.csv?accessType=DOWNLOAD') + .replyWithFile( + 200, + path.resolve( + __dirname, + '../test/assets/Electric_Vehicle_Test_Data.csv', + ), + { + 'Content-Type': 'text/csv', + }, + ), + ]; + }); + postgresLoaderMock.setup(); + sqliteLoaderMock.setup(); + + await runAction(path.resolve(baseDir, 'electric-vehicles.jv'), { + ...defaultOptions, + env: new Map<string, string>([ + ['DB_HOST', 'mock'], + ['DB_DATABASE', 'mock'], + ['DB_PASSWORD', 'mock'], + ['DB_USERNAME', 'mock'], + ['DB_PORT', '5432'], + ]), + }); + + expect(httpExtractorMock.nockScopes.every((scope) => scope.isDone())); + expect(postgresLoaderMock.pgClient.connect).toBeCalledTimes(1); + expect(postgresLoaderMock.pgClient.query).toBeCalledTimes(3); + expect(postgresLoaderMock.pgClient.end).toBeCalledTimes(1); + expect(sqliteLoaderMock.sqliteClient.run).toBeCalledTimes(3); + expect(sqliteLoaderMock.sqliteClient.close).toBeCalledTimes(1); + + expect(exitSpy).toHaveBeenCalledWith(0); + }); + + it('should have no errors when executing gtfs-rt.jv example', async () => { + // Prepare mocks + httpExtractorMock.setup(() => { + return [ + nock('https://proxy.transport.data.gouv.fr') + .get('/resource/bibus-brest-gtfs-rt-trip-update') + .replyWithFile( + 200, + path.resolve( + __dirname, + '../test/assets/bibus-brest-gtfs-rt-trip-update', + ), + { + 'Content-Type': 'application/octet-stream', + }, + ) + .get('/resource/bibus-brest-gtfs-rt-vehicle-position') + .replyWithFile( + 200, + path.resolve( + __dirname, + '../test/assets/bibus-brest-gtfs-rt-vehicle-position', + ), + { + 'Content-Type': 'application/octet-stream', + }, + ) + .get('/resource/bibus-brest-gtfs-rt-alerts') + .replyWithFile( + 200, + path.resolve( + __dirname, + '../test/assets/bibus-brest-gtfs-rt-alerts.json', + ), + { + 'Content-Type': 'application/json', + }, + ), + ]; + }); + sqliteLoaderMock.setup(); + + await runAction(path.resolve(baseDir, 'gtfs-rt.jv'), { + ...defaultOptions, + }); + + expect(httpExtractorMock.nockScopes.every((scope) => scope.isDone())); + expect(sqliteLoaderMock.sqliteClient.run).toBeCalledTimes(6); + expect(sqliteLoaderMock.sqliteClient.close).toBeCalledTimes(3); + + expect(exitSpy).toHaveBeenCalledWith(0); + }); + + it('should have no errors when executing gtfs-static.jv example', async () => { + // Prepare mocks + httpExtractorMock.setup(() => { + return [ + nock('https://developers.google.com') + .get('/static/transit/gtfs/examples/sample-feed.zip') + .replyWithFile( + 200, + path.resolve(__dirname, '../test/assets/sample-feed.zip'), + { + 'Content-Type': 'application/zip', + }, + ), + ]; + }); + sqliteLoaderMock.setup(); + + await runAction(path.resolve(baseDir, 'gtfs-static.jv'), { + ...defaultOptions, + }); + + expect(httpExtractorMock.nockScopes.every((scope) => scope.isDone())); + expect(sqliteLoaderMock.sqliteClient.run).toBeCalledTimes(33); + expect(sqliteLoaderMock.sqliteClient.close).toBeCalledTimes(11); + + expect(exitSpy).toHaveBeenCalledWith(0); + }); +}); diff --git a/apps/interpreter/src/index.ts b/apps/interpreter/src/index.ts new file mode 100644 index 00000000..1ddbbdc1 --- /dev/null +++ b/apps/interpreter/src/index.ts @@ -0,0 +1,75 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { DebugGranularityValues } from '@jvalue/jayvee-execution'; +import { Command } from 'commander'; + +import { version as packageJsonVersion } from '../package.json'; + +import { assertNodeVersion } from './prerequisites'; +import { runAction } from './run-action'; + +const runtimeParameterRegex = /^([_a-zA-Z][\w_]*)=(.*)$/; +function collectRuntimeParameters( + optionValue: string, + previous: Map<string, string>, +): Map<string, string> { + const regexMatch = optionValue.match(runtimeParameterRegex); + if (regexMatch == null) { + throw new Error( + `Encountered runtime parameter with invalid syntax: ${optionValue}`, + ); + } + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const parameter = regexMatch[1]!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const value = regexMatch[2]!; + + previous.set(parameter, value); + return previous; +} + +assertNodeVersion(); +const program = new Command(); + +const version: string = packageJsonVersion; +program.version(version); + +program + .argument('<file>', `path to the .jv source file`) + .option( + '-e, --env <parameter>=<value>', + 'provide a runtime parameters', + collectRuntimeParameters, + new Map<string, string>(), + ) + .option('-d, --debug', 'enable debug logging', false) + .option( + '-dg, --debug-granularity <granularity>', + `Sets the granularity of block debug logging. Can have values ${DebugGranularityValues.join( + ', ', + )} (default).`, + DebugGranularityValues[DebugGranularityValues.length - 1], + ) + .option( + '-dt, --debug-target <block name>', + `Sets the target blocks of the of block debug logging, separated by comma. If not given, all blocks are targeted.`, + undefined, + ) + .option( + '-po, --parse-only', + 'Only parses the model without running it. Exits with 0 if the model is valid, with 1 otherwise.', + false, + ) + .option( + '--use-polars', + 'WARNING: THIS IS EXPERIMENTAL AND PROBABLY BROKEN: Use the experimental polars backend instead of the typescript native implementation.', + false, + ) + .description('Run a Jayvee file') + .action(runAction); + +program.showHelpAfterError(); + +program.parse(process.argv); diff --git a/apps/interpreter/src/parse-only.spec.ts b/apps/interpreter/src/parse-only.spec.ts new file mode 100644 index 00000000..01e56427 --- /dev/null +++ b/apps/interpreter/src/parse-only.spec.ts @@ -0,0 +1,81 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import fs from 'node:fs'; +import path from 'node:path'; +import process from 'node:process'; + +import { + type RunOptions, + interpretModel, + interpretString, +} from '@jvalue/jayvee-interpreter-lib'; +import { vi } from 'vitest'; + +import { runAction } from './run-action'; + +vi.mock('@jvalue/jayvee-interpreter-lib', async () => { + const original: object = await vi.importActual( + '@jvalue/jayvee-interpreter-lib', + ); + return { + ...original, + interpretModel: vi.fn(), + interpretString: vi.fn(), + }; +}); + +describe('Parse Only', () => { + const pathToValidModel = path.resolve(__dirname, '../../../example/cars.jv'); + const pathToInvalidModel = path.resolve( + __dirname, + '../test/assets/broken-model.jv', + ); + + const defaultOptions: RunOptions = { + env: new Map<string, string>(), + debug: false, + debugGranularity: 'minimal', + debugTarget: undefined, + }; + + afterEach(() => { + // Assert that model is not executed + expect(interpretString).not.toBeCalled(); + expect(interpretModel).not.toBeCalled(); + }); + + beforeEach(() => { + vi.clearAllMocks(); + vi.spyOn(process, 'exit').mockImplementation(() => { + throw new Error(); + }); + }); + + it('should exit with 0 on a valid option', async () => { + await expect( + runAction(pathToValidModel, { + ...defaultOptions, + parseOnly: true, + }), + ).rejects.toBeDefined(); + + expect(process.exit).toBeCalledTimes(1); + expect(process.exit).toHaveBeenCalledWith(0); + }); + + it('should exit with 1 on error', async () => { + expect(fs.existsSync(pathToInvalidModel)).toBe(true); + + await expect( + runAction(pathToInvalidModel, { + ...defaultOptions, + parseOnly: true, + }), + ).rejects.toBeDefined(); + + expect(process.exit).toBeCalledTimes(1); + expect(process.exit).toHaveBeenCalledWith(1); + }); +}); diff --git a/apps/interpreter/src/prerequisites.ts b/apps/interpreter/src/prerequisites.ts new file mode 100644 index 00000000..4721e0cd --- /dev/null +++ b/apps/interpreter/src/prerequisites.ts @@ -0,0 +1,39 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** + * Asserts that a certain node version is installed on the executing machine. + * Exits the process if this prerequisite is not fulfilled. + */ +export function assertNodeVersion() { + const requiredNodeMajorVersion = 17; + const currentNodeMajorVersion = getNodeMajorVersion(); + + if (currentNodeMajorVersion < requiredNodeMajorVersion) { + console.error( + `Jayvee requires node version ${requiredNodeMajorVersion}.0.0 or higher.`, + ); + console.info( + `Your current node version is ${currentNodeMajorVersion} - please upgrade!`, + ); + process.exit(1); + } +} + +/** + * Returns the node version of the executing machine. + * Exits the process if no node process is running. + */ +function getNodeMajorVersion(): number { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + const nodeVersion = process?.versions?.node; + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (nodeVersion === undefined) { + console.error('Could not find a nodejs runtime.'); + process.exit(1); + } + + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + return +nodeVersion.split('.')[0]!; +} diff --git a/apps/interpreter/src/run-action.ts b/apps/interpreter/src/run-action.ts new file mode 100644 index 00000000..a461f53f --- /dev/null +++ b/apps/interpreter/src/run-action.ts @@ -0,0 +1,39 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import process from 'node:process'; + +import { + type LoggerFactory, + type RunOptions, + extractAstNodeFromFile, + interpretModel, + parseModel, +} from '@jvalue/jayvee-interpreter-lib'; +import { + type JayveeModel, + type JayveeServices, +} from '@jvalue/jayvee-language-server'; + +export async function runAction( + fileName: string, + options: RunOptions, +): Promise<void> { + const extractAstNodeFn = async ( + services: JayveeServices, + loggerFactory: LoggerFactory, + ) => + await extractAstNodeFromFile<JayveeModel>( + fileName, + services, + loggerFactory.createLogger(), + ); + if (options.parseOnly === true) { + const { model, services } = await parseModel(extractAstNodeFn, options); + const exitCode = model != null && services != null ? 0 : 1; + process.exit(exitCode); + } + const exitCode = await interpretModel(extractAstNodeFn, options); + process.exit(exitCode); +} diff --git a/apps/interpreter/test/assets/Electric_Vehicle_Test_Data.csv b/apps/interpreter/test/assets/Electric_Vehicle_Test_Data.csv new file mode 100644 index 00000000..2bb7ee2e --- /dev/null +++ b/apps/interpreter/test/assets/Electric_Vehicle_Test_Data.csv @@ -0,0 +1,11 @@ +VIN (1-10),County,City,State,Postal Code,Model Year,Make,Model,Electric Vehicle Type,Clean Alternative Fuel Vehicle (CAFV) Eligibility,Electric Range,Base MSRP,Legislative District,DOL Vehicle ID,Vehicle Location,Electric Utility,2020 Census Tract +3S4Y3G2Y5H,ImaginaryCounty1,ImaginaryCity1,ImaginaryState1,11111,2019,TESLA,MODEL 3,Battery Electric Vehicle (BEV),Clean Alternative Fuel Vehicle Eligible,220,0,22,242565116,POINT (-122.91330514434256 47.01362878499456),PUGET SOUND ENERGY INC,53067010910 +5U6N7E9C1P,ImaginaryCounty2,ImaginaryCity2,ImaginaryState2,22222,2022,NISSAN,LEAF,Battery Electric Vehicle (BEV),Eligibility unknown as battery range has not been researched,0,0,10,183272785,POINT (-122.91322850862442 47.01360234311947),PUGET SOUND ENERGY INC,53029972000 +1H3T4M8A6X,ImaginaryCounty3,ImaginaryCity3,ImaginaryState3,33333,2020,TESLA,MODEL 3,Battery Electric Vehicle (BEV),Clean Alternative Fuel Vehicle Eligible,266,0,44,112552366,POINT (-122.91312615963526 47.0135512649186),PUGET SOUND ENERGY INC,53061052502 +7T5Y6G4E2B,ImaginaryCounty4,ImaginaryCity4,ImaginaryState4,44444,2020,TESLA,MODEL 3,Battery Electric Vehicle (BEV),Clean Alternative Fuel Vehicle Eligible,322,0,11,6336319,POINT (-122.91308775619064 47.013591973859774),CITY OF SEATTLE - (WA)|CITY OF TACOMA - (WA),53033009300 +9P2O1I8U7I,ImaginaryCounty5,ImaginaryCity5,ImaginaryState5,55555,2013,TESLA,MODEL S,Battery Electric Vehicle (BEV),Clean Alternative Fuel Vehicle Eligible,208,69900,21,186212960,POINT (-122.91320961821407 47.01360621014777),PUGET SOUND ENERGY INC,53061050403 +6Y3J1B0K9X,ImaginaryCounty6,ImaginaryCity6,ImaginaryState6,66666,2018,BMW,I3,Plug-in Hybrid Electric Vehicle (PHEV),Clean Alternative Fuel Vehicle Eligible,97,0,12,215122904,POINT (-122.913131342184 47.01357200163717),PUD NO 1 OF CHELAN COUNTY,53007960400 +2J6N8E0C7L,ImaginaryCounty7,ImaginaryCity7,ImaginaryState7,77777,2020,TESLA,MODEL 3,Battery Electric Vehicle (BEV),Clean Alternative Fuel Vehicle Eligible,308,0,38,110992472,POINT (-122.91324264805215 47.013600981646336),PUGET SOUND ENERGY INC,53061053102 +4O1A7R2L6S,ImaginaryCounty8,ImaginaryCity8,ImaginaryState8,88888,2021,AUDI,Q5 E,Plug-in Hybrid Electric Vehicle (PHEV),Not eligible due to low battery range,18,0,21,138909032,POINT (-122.9132902458624 47.013584303604186),PUGET SOUND ENERGY INC,53061050300 +5K8W4H3E1F,ImaginaryCounty9,ImaginaryCity9,ImaginaryState9,99999,2019,TOYOTA,PRIUS PRIME,Plug-in Hybrid Electric Vehicle (PHEV),Not eligible due to low battery range,25,0,22,272310279,POINT (-122.91317007100271 47.01355510852546),PUGET SOUND ENERGY INC,53067010700 +3J2P1Z8A9N,ImaginaryCounty10,ImaginaryCity10,ImaginaryState10,10101,2018,NISSAN,LEAF,Battery Electric Vehicle (BEV),Clean Alternative Fuel Vehicle Eligible,151,0,22,235573929,POINT (-122.91319623770842 47.013576180244146),PUGET SOUND ENERGY INC,53067011422 diff --git a/apps/interpreter/test/assets/Electric_Vehicle_Test_Data.csv.license b/apps/interpreter/test/assets/Electric_Vehicle_Test_Data.csv.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/interpreter/test/assets/Electric_Vehicle_Test_Data.csv.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/interpreter/test/assets/bibus-brest-gtfs-rt-alerts.json b/apps/interpreter/test/assets/bibus-brest-gtfs-rt-alerts.json new file mode 100644 index 00000000..aebc307d Binary files /dev/null and b/apps/interpreter/test/assets/bibus-brest-gtfs-rt-alerts.json differ diff --git a/apps/interpreter/test/assets/bibus-brest-gtfs-rt-alerts.json.license b/apps/interpreter/test/assets/bibus-brest-gtfs-rt-alerts.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/interpreter/test/assets/bibus-brest-gtfs-rt-alerts.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/interpreter/test/assets/bibus-brest-gtfs-rt-trip-update b/apps/interpreter/test/assets/bibus-brest-gtfs-rt-trip-update new file mode 100644 index 00000000..14823623 Binary files /dev/null and b/apps/interpreter/test/assets/bibus-brest-gtfs-rt-trip-update differ diff --git a/apps/interpreter/test/assets/bibus-brest-gtfs-rt-trip-update.license b/apps/interpreter/test/assets/bibus-brest-gtfs-rt-trip-update.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/interpreter/test/assets/bibus-brest-gtfs-rt-trip-update.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/interpreter/test/assets/bibus-brest-gtfs-rt-vehicle-position b/apps/interpreter/test/assets/bibus-brest-gtfs-rt-vehicle-position new file mode 100644 index 00000000..944ad0dc Binary files /dev/null and b/apps/interpreter/test/assets/bibus-brest-gtfs-rt-vehicle-position differ diff --git a/apps/interpreter/test/assets/bibus-brest-gtfs-rt-vehicle-position.license b/apps/interpreter/test/assets/bibus-brest-gtfs-rt-vehicle-position.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/interpreter/test/assets/bibus-brest-gtfs-rt-vehicle-position.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/interpreter/test/assets/broken-model.jv b/apps/interpreter/test/assets/broken-model.jv new file mode 100644 index 00000000..447eab7c --- /dev/null +++ b/apps/interpreter/test/assets/broken-model.jv @@ -0,0 +1,54 @@ +// SPDX-FileCopyrightText: 2024 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline CarsPipeline { + // Try using a CoolCarsExtractor although we only have normal cars. + // This fill result in an error during parsing. + CoolCarsExtractor -> CarsTextFileInterpreter; + + CarsTextFileInterpreter + -> CarsCSVInterpreter + -> NameHeaderWriter + -> CarsTableInterpreter + -> CarsLoader; + + block CarsExtractor oftype HttpExtractor { + url: "https://gist.githubusercontent.com/noamross/e5d3e859aa0c794be10b/raw/b999fb4425b54c63cab088c0ce2c0d6ce961a563/cars.csv"; + } + + block CarsTextFileInterpreter oftype TextFileInterpreter { } + + block CarsCSVInterpreter oftype CSVInterpreter { + enclosing: '"'; + } + + block NameHeaderWriter oftype CellWriter { + at: cell A1; + write: ["name"]; + } + + block CarsTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "name" oftype text, + "mpg" oftype decimal, + "cyl" oftype integer, + "disp" oftype decimal, + "hp" oftype integer, + "drat" oftype decimal, + "wt" oftype decimal, + "qsec" oftype decimal, + "vs" oftype integer, + "am" oftype integer, + "gear" oftype integer, + "carb" oftype integer + ]; + } + + block CarsLoader oftype SQLiteLoader { + table: "Cars"; + file: "./cars.sqlite"; + } + +} \ No newline at end of file diff --git a/apps/interpreter/test/assets/cars.csv b/apps/interpreter/test/assets/cars.csv new file mode 100644 index 00000000..d783f8d6 --- /dev/null +++ b/apps/interpreter/test/assets/cars.csv @@ -0,0 +1,11 @@ +"","mpg","cyl","disp","hp","drat","wt","qsec","vs","am","gear","carb" +"Test Car 1",20,4,140,100,3.5,2.8,18,1,0,4,2 +"Test Car 2",18,6,200,120,3.9,3.2,19,1,1,4,4 +"Test Car 3",15,8,350,180,3.2,3.7,16,0,0,3,2 +"Test Car 4",25,4,120,80,4.1,2.5,20,1,1,4,1 +"Test Car 5",22,4,160,110,3.8,2.9,17,0,1,5,2 +"Test Car 6",21,6,200,130,3.6,3.1,18.5,0,0,3,2 +"Test Car 7",19,6,225,150,3.3,3.5,16.5,1,0,4,4 +"Test Car 8",17,8,400,250,2.9,4,15,0,0,3,2 +"Test Car 9",23,4,140,95,4.2,2.6,19.5,1,1,4,1 +"Test Car 10",27,4,110,75,4.5,2.2,21,0,1,5,2 diff --git a/apps/interpreter/test/assets/cars.csv.license b/apps/interpreter/test/assets/cars.csv.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/interpreter/test/assets/cars.csv.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/interpreter/test/assets/gtfs.zip b/apps/interpreter/test/assets/gtfs.zip new file mode 100644 index 00000000..26be4ff1 Binary files /dev/null and b/apps/interpreter/test/assets/gtfs.zip differ diff --git a/apps/interpreter/test/assets/gtfs.zip.license b/apps/interpreter/test/assets/gtfs.zip.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/interpreter/test/assets/gtfs.zip.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/interpreter/test/assets/sample-feed.zip b/apps/interpreter/test/assets/sample-feed.zip new file mode 100644 index 00000000..f188507c Binary files /dev/null and b/apps/interpreter/test/assets/sample-feed.zip differ diff --git a/apps/interpreter/test/assets/sample-feed.zip.license b/apps/interpreter/test/assets/sample-feed.zip.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/interpreter/test/assets/sample-feed.zip.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/interpreter/tsconfig.app.json b/apps/interpreter/tsconfig.app.json new file mode 100644 index 00000000..b6f711b0 --- /dev/null +++ b/apps/interpreter/tsconfig.app.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "resolveJsonModule": true, + "types": ["node"] + }, + "exclude": ["vite.config.ts", "**/*.spec.ts", "**/*.test.ts"], + "include": ["**/*.ts"] +} diff --git a/apps/interpreter/tsconfig.app.json.license b/apps/interpreter/tsconfig.app.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/interpreter/tsconfig.app.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/interpreter/tsconfig.json b/apps/interpreter/tsconfig.json new file mode 100644 index 00000000..63dbe35f --- /dev/null +++ b/apps/interpreter/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../tsconfig.base.json", + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/apps/interpreter/tsconfig.json.license b/apps/interpreter/tsconfig.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/interpreter/tsconfig.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/interpreter/tsconfig.spec.json b/apps/interpreter/tsconfig.spec.json new file mode 100644 index 00000000..b201da93 --- /dev/null +++ b/apps/interpreter/tsconfig.spec.json @@ -0,0 +1,27 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "types": [ + "vitest/globals", + "vitest/importMeta", + "vite/client", + "node", + "vitest" + ] + }, + "include": [ + "vite.config.ts", + "vitest.config.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.test.tsx", + "src/**/*.spec.tsx", + "src/**/*.test.js", + "src/**/*.spec.js", + "src/**/*.test.jsx", + "src/**/*.spec.jsx", + "src/**/*.d.ts", + "test/**/*.ts" + ] +} diff --git a/apps/interpreter/tsconfig.spec.json.license b/apps/interpreter/tsconfig.spec.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/interpreter/tsconfig.spec.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/interpreter/vite.config.ts b/apps/interpreter/vite.config.ts new file mode 100644 index 00000000..c5336a4a --- /dev/null +++ b/apps/interpreter/vite.config.ts @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// / <reference types='vitest' /> +import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + root: __dirname, + cacheDir: '../../node_modules/.vite/apps/interpreter', + + plugins: [nxViteTsPaths()], + + test: { + globals: true, + cacheDir: '../../node_modules/.vitest', + environment: 'jsdom', + include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], + + reporters: ['default'], + coverage: { + reportsDirectory: '../../coverage/apps/interpreter', + provider: 'v8', + }, + }, +}); diff --git a/apps/language-server-web-worker/.babelrc b/apps/language-server-web-worker/.babelrc new file mode 100644 index 00000000..fd4cbcde --- /dev/null +++ b/apps/language-server-web-worker/.babelrc @@ -0,0 +1,10 @@ +{ + "presets": [ + [ + "@nx/js/babel", + { + "useBuiltIns": "usage" + } + ] + ] +} diff --git a/apps/language-server-web-worker/.babelrc.license b/apps/language-server-web-worker/.babelrc.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/language-server-web-worker/.babelrc.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/language-server-web-worker/.eslintrc.json b/apps/language-server-web-worker/.eslintrc.json new file mode 100644 index 00000000..eb39373f --- /dev/null +++ b/apps/language-server-web-worker/.eslintrc.json @@ -0,0 +1,21 @@ +{ + "extends": ["../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "parserOptions": { + "project": ["apps/language-server-web-worker/tsconfig.app.json"] + }, + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/apps/language-server-web-worker/.eslintrc.json.license b/apps/language-server-web-worker/.eslintrc.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/language-server-web-worker/.eslintrc.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/language-server-web-worker/README.md b/apps/language-server-web-worker/README.md new file mode 100644 index 00000000..b492f13c --- /dev/null +++ b/apps/language-server-web-worker/README.md @@ -0,0 +1,11 @@ +<!-- +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only +--> + +# Language server Web Worker + +Instantiates a Jayvee language server that is meant to run in a [Web Worker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API). It can be used together with the +[monaco editor](../../libs/monaco-editor) to edit Jayvee files in a browser. Have a look at the [documentation](../../libs/monaco-editor/README.md) of the monaco editor for +details on how to set them up together. diff --git a/apps/language-server-web-worker/package.json b/apps/language-server-web-worker/package.json new file mode 100644 index 00000000..b62a0d74 --- /dev/null +++ b/apps/language-server-web-worker/package.json @@ -0,0 +1,16 @@ +{ + "name": "@jvalue/jayvee-language-server-web-worker", + "main": "main.js", + "bugs": { + "url": "https://github.com/jvalue/jayvee/issues" + }, + "repository": { + "type": "git", + "url": "https://github.com/jvalue/jayvee.git" + }, + "homepage": "https://github.com/jvalue/jayvee", + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org" + } +} diff --git a/apps/language-server-web-worker/package.json.license b/apps/language-server-web-worker/package.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/language-server-web-worker/package.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/language-server-web-worker/project.json b/apps/language-server-web-worker/project.json new file mode 100644 index 00000000..26554814 --- /dev/null +++ b/apps/language-server-web-worker/project.json @@ -0,0 +1,52 @@ +{ + "name": "language-server-web-worker", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "apps/language-server-web-worker/src", + "projectType": "application", + "targets": { + "build": { + "options": { + "main": "{projectRoot}/src/main.ts", + "tsConfig": "{projectRoot}/tsconfig.app.json", + "generatePackageJson": true, + "platform": "browser", + "vendorChunk": false, + "runtimeChunk": false + } + }, + "serve": { + "executor": "@nx/js:node", + "options": { + "buildTarget": "language-server-web-worker:build" + } + }, + "lint": { + "executor": "@nx/eslint:lint", + "outputs": ["{options.outputFile}"] + }, + "pre-publish": { + "executor": "nx:run-commands", + "dependsOn": ["build"], + "options": { + "commands": [ + // Manually delete all dependencies in package.json because the "externalDependencies": "none" setting in "build" above seems to have no effect on the generated package.json + "node tools/scripts/delete-dependencies.mjs language-server-web-worker", + "node tools/scripts/add-package-json-version.mjs language-server-web-worker", + "node tools/scripts/publish.mjs language-server-web-worker false" // dry-run + ], + "parallel": false + } + }, + "publish": { + "executor": "nx:run-commands", + "dependsOn": ["pre-publish"], + "options": { + "commands": [ + "node tools/scripts/publish.mjs language-server-web-worker true" + ], + "parallel": false + } + } + }, + "tags": [] +} diff --git a/apps/language-server-web-worker/project.json.license b/apps/language-server-web-worker/project.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/language-server-web-worker/project.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/language-server-web-worker/src/main.ts b/apps/language-server-web-worker/src/main.ts new file mode 100644 index 00000000..d532ac45 --- /dev/null +++ b/apps/language-server-web-worker/src/main.ts @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { createJayveeServices } from '@jvalue/jayvee-language-server'; +import { EmptyFileSystem } from 'langium'; +import { startLanguageServer } from 'langium/lsp'; +import { + BrowserMessageReader, + BrowserMessageWriter, + createConnection, +} from 'vscode-languageserver/browser'; + +declare const self: DedicatedWorkerGlobalScope; + +const messageReader = new BrowserMessageReader(self); +const messageWriter = new BrowserMessageWriter(self); + +const connection = createConnection(messageReader, messageWriter); + +const { shared } = createJayveeServices({ connection, ...EmptyFileSystem }); + +startLanguageServer(shared); diff --git a/apps/language-server-web-worker/tsconfig.app.json b/apps/language-server-web-worker/tsconfig.app.json new file mode 100644 index 00000000..020db56c --- /dev/null +++ b/apps/language-server-web-worker/tsconfig.app.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "esnext", + "lib": ["esnext", "DOM", "WebWorker"], + }, + "exclude": ["src/**/*.spec.ts", "src/**/*.test.ts"], + "include": ["src/**/*.ts"] +} diff --git a/apps/language-server-web-worker/tsconfig.app.json.license b/apps/language-server-web-worker/tsconfig.app.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/language-server-web-worker/tsconfig.app.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/language-server-web-worker/tsconfig.json b/apps/language-server-web-worker/tsconfig.json new file mode 100644 index 00000000..816e3f36 --- /dev/null +++ b/apps/language-server-web-worker/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.base.json", + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.app.json" + } + ] +} diff --git a/apps/language-server-web-worker/tsconfig.json.license b/apps/language-server-web-worker/tsconfig.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/language-server-web-worker/tsconfig.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/vs-code-extension/.eslintrc.json b/apps/vs-code-extension/.eslintrc.json new file mode 100644 index 00000000..1a122c7c --- /dev/null +++ b/apps/vs-code-extension/.eslintrc.json @@ -0,0 +1,21 @@ +{ + "extends": ["../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "parserOptions": { + "project": ["apps/vs-code-extension/tsconfig.app.json"] + }, + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/apps/vs-code-extension/.eslintrc.json.license b/apps/vs-code-extension/.eslintrc.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/vs-code-extension/.eslintrc.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/vs-code-extension/.gitignore b/apps/vs-code-extension/.gitignore new file mode 100644 index 00000000..6806daaa --- /dev/null +++ b/apps/vs-code-extension/.gitignore @@ -0,0 +1,6 @@ +# SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +# +# SPDX-License-Identifier: AGPL-3.0-only + +# file generated by Langium +assets/jayvee.tmLanguage.json diff --git a/apps/vs-code-extension/README.md b/apps/vs-code-extension/README.md new file mode 100644 index 00000000..ead81b74 --- /dev/null +++ b/apps/vs-code-extension/README.md @@ -0,0 +1,13 @@ +<!-- +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only +--> + +# VS Code Extension + +## Important project files + +- `package.json` - the manifest file where the language support is declared. +- `src/extension.ts` - the main code of the extension, which is responsible for launching a language server and client. +- `src/language-server.ts` - the entry point of the language server process. diff --git a/apps/vs-code-extension/assets/.vscodeignore b/apps/vs-code-extension/assets/.vscodeignore new file mode 100644 index 00000000..dc9ebea7 --- /dev/null +++ b/apps/vs-code-extension/assets/.vscodeignore @@ -0,0 +1,5 @@ +# SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +# +# SPDX-License-Identifier: AGPL-3.0-only + +*.map \ No newline at end of file diff --git a/apps/vs-code-extension/assets/language-configuration.json b/apps/vs-code-extension/assets/language-configuration.json new file mode 100644 index 00000000..2b1e4e94 --- /dev/null +++ b/apps/vs-code-extension/assets/language-configuration.json @@ -0,0 +1,30 @@ +{ + "comments": { + // symbol used for single line comment. Remove this entry if your language does not support line comments + "lineComment": "//", + // symbols used for start and end a block comment. Remove this entry if your language does not support block comments + "blockComment": ["/*", "*/"] + }, + // symbols used as brackets + "brackets": [ + ["{", "}"], + ["[", "]"], + ["(", ")"] + ], + // symbols that are auto closed when typing + "autoClosingPairs": [ + ["{", "}"], + ["[", "]"], + ["(", ")"], + ["\"", "\""], + ["'", "'"] + ], + // symbols that can be used to surround a selection + "surroundingPairs": [ + ["{", "}"], + ["[", "]"], + ["(", ")"], + ["\"", "\""], + ["'", "'"] + ] +} diff --git a/apps/vs-code-extension/assets/language-configuration.json.license b/apps/vs-code-extension/assets/language-configuration.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/vs-code-extension/assets/language-configuration.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/vs-code-extension/package.json b/apps/vs-code-extension/package.json new file mode 100644 index 00000000..a80c68ce --- /dev/null +++ b/apps/vs-code-extension/package.json @@ -0,0 +1,46 @@ +{ + "name": "jayvee", + "displayName": "Jayvee", + "description": "Support for the Jayvee language", + "engines": { + "vscode": "^1.56.0" + }, + "categories": [ + "Programming Languages" + ], + "contributes": { + "languages": [ + { + "id": "jayvee", + "aliases": [ + "Jayvee", + "jayvee" + ], + "extensions": [ + ".jv" + ], + "configuration": "language-configuration.json" + } + ], + "grammars": [ + { + "language": "jayvee", + "scopeName": "source.jayvee", + "path": "jayvee.tmLanguage.json" + } + ] + }, + "activationEvents": [ + "onLanguage:jayvee" + ], + "main": "extension.cjs", + "bugs": { + "url": "https://github.com/jvalue/jayvee/issues" + }, + "repository": { + "type": "git", + "url": "https://github.com/jvalue/jayvee.git" + }, + "homepage": "https://github.com/jvalue/jayvee", + "version": "0.0.0" +} diff --git a/apps/vs-code-extension/package.json.license b/apps/vs-code-extension/package.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/vs-code-extension/package.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/vs-code-extension/project.json b/apps/vs-code-extension/project.json new file mode 100644 index 00000000..74cd987c --- /dev/null +++ b/apps/vs-code-extension/project.json @@ -0,0 +1,75 @@ +{ + "name": "vs-code-extension", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "apps/vs-code-extension/src", + "projectType": "application", + "targets": { + "build": { + "options": { + "tsConfig": "{projectRoot}/tsconfig.app.json", + "main": "{projectRoot}/src/extension.ts", + "outputFileName": "", + "additionalEntryPoints": [ + "{projectRoot}/src/language-server.ts" + ], + "external": ["vscode"], + "platform": "node", + "format": ["cjs"], + "assets": [ + { + "input": "{projectRoot}/assets", + "glob": "*", + "output": "/" + } + ], + "thirdParty": true + }, + "configurations": { + "dev": { + "esbuildOptions": { + // Required, so VS Code's debugger is able to attach when running the extension + "sourcemap": "linked" + }, + "minify": false + }, + "prod": { + "esbuildOptions": { + "sourcemap": false + }, + "minify": true + } + }, + "defaultConfiguration": "dev" + }, + "lint": { + "executor": "@nx/eslint:lint", + "outputs": ["{options.outputFile}"] + }, + "pack": { + "executor": "nx:run-commands", + "dependsOn": ["build"], + "options": { + "commands": [ + // Delete vscode dependency in package.json which was generated by Nx + "node tools/scripts/delete-dependencies.mjs vs-code-extension", + // Add an empty license file, so vsce does not complain during packaging + "touch dist/apps/vs-code-extension/LICENSE", + "node tools/scripts/add-package-json-version.mjs vs-code-extension", + "cd dist/apps/vs-code-extension && npx vsce package --out jayvee.vsix" + ], + "parallel": false + } + }, + "install": { + "executor": "nx:run-commands", + "dependsOn": ["pack"], + "options": { + "commands": [ + "code --install-extension dist/apps/vs-code-extension/jayvee.vsix" + ], + "parallel": false + } + } + }, + "tags": [] +} diff --git a/apps/vs-code-extension/project.json.license b/apps/vs-code-extension/project.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/vs-code-extension/project.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/vs-code-extension/src/extension.ts b/apps/vs-code-extension/src/extension.ts new file mode 100644 index 00000000..770e0132 --- /dev/null +++ b/apps/vs-code-extension/src/extension.ts @@ -0,0 +1,78 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import path from 'node:path'; + +import { type ExtensionContext, workspace } from 'vscode'; +import { + LanguageClient, + type LanguageClientOptions, + type ServerOptions, + TransportKind, +} from 'vscode-languageclient/node'; + +import { StandardLibraryFileSystemProvider } from './standard-library-file-system-provider'; + +let client: LanguageClient; + +// This function is called when the extension is activated. +export function activate(context: ExtensionContext): void { + StandardLibraryFileSystemProvider.register(context); + client = startLanguageClient(context); +} + +// This function is called when the extension is deactivated. +export function deactivate(): Thenable<void> | undefined { + return client.stop(); +} + +function startLanguageClient(context: ExtensionContext): LanguageClient { + const serverModule = context.asAbsolutePath(path.join('language-server.cjs')); + // The debug options for the server + // --inspect=6009: runs the server in Node's Inspector mode so VS Code can attach to the server for debugging. + // By setting `process.env.DEBUG_BREAK` to a truthy value, the language server will wait until a debugger is attached. + const debugOptions = { + execArgv: [ + '--nolazy', + `--inspect${process.env.DEBUG_BREAK !== undefined ? '-brk' : ''}=${ + process.env.DEBUG_SOCKET ?? '6009' + }`, + ], + }; + + // If the extension is launched in debug mode then the debug server options are used + // Otherwise the run options are used + const serverOptions: ServerOptions = { + run: { module: serverModule, transport: TransportKind.ipc }, + debug: { + module: serverModule, + transport: TransportKind.ipc, + options: debugOptions, + }, + }; + + const fileSystemWatcher = workspace.createFileSystemWatcher('**/*.jv'); + context.subscriptions.push(fileSystemWatcher); + + // Options to control the language client + const clientOptions: LanguageClientOptions = { + documentSelector: [{ scheme: 'file', language: 'jayvee' }], + synchronize: { + // Notify the server about file changes to files contained in the workspace + fileEvents: fileSystemWatcher, + }, + }; + + // Create the language client and start the client. + const client = new LanguageClient( + 'jayvee', + 'Jayvee', + serverOptions, + clientOptions, + ); + + // Start the client. This will also launch the server + void client.start(); + return client; +} diff --git a/apps/vs-code-extension/src/language-server.ts b/apps/vs-code-extension/src/language-server.ts new file mode 100644 index 00000000..2c411483 --- /dev/null +++ b/apps/vs-code-extension/src/language-server.ts @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { createJayveeServices } from '@jvalue/jayvee-language-server'; +import { startLanguageServer } from 'langium/lsp'; +import { NodeFileSystem } from 'langium/node'; +import { ProposedFeatures, createConnection } from 'vscode-languageserver/node'; + +// Create a connection to the client +const connection = createConnection(ProposedFeatures.all); + +// Inject the shared services and language-specific services +const { shared } = createJayveeServices({ + connection, + ...NodeFileSystem, +}); + +// Start the language server with the shared services +startLanguageServer(shared); diff --git a/apps/vs-code-extension/src/standard-library-file-system-provider.ts b/apps/vs-code-extension/src/standard-library-file-system-provider.ts new file mode 100644 index 00000000..dfc6fa1d --- /dev/null +++ b/apps/vs-code-extension/src/standard-library-file-system-provider.ts @@ -0,0 +1,109 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { getStdLib } from '@jvalue/jayvee-language-server'; +import { + EventEmitter, + type ExtensionContext, + type FileChangeEvent, + type FileStat, + FileSystemError, + type FileSystemProvider, + FileType, + Uri, + workspace, +} from 'vscode'; + +export class StandardLibraryFileSystemProvider implements FileSystemProvider { + private libraries = new Map<string, Buffer>(); + + // The following class members only serve to satisfy the interface: + private readonly didChangeFile = new EventEmitter<FileChangeEvent[]>(); + onDidChangeFile = this.didChangeFile.event; + + constructor() { + this.registerStdLib(); + } + + private registerStdLib() { + // The VSCode Extension needs to register the StdLangExtension, + // otherwise the StdLib does not include the block type definitions. + + Object.entries(getStdLib()).forEach(([libName, lib]) => { + this.libraries.set( + Uri.parse(libName).toString(), // removes slashes if missing authorities, required for matching later on + Buffer.from(lib), + ); + }); + } + + static register(context: ExtensionContext) { + context.subscriptions.push( + workspace.registerFileSystemProvider( + 'builtin', + new StandardLibraryFileSystemProvider(), + { + isReadonly: true, + isCaseSensitive: false, + }, + ), + ); + } + + stat(uri: Uri): FileStat { + const libBuffer = this.getLibrary(uri); + const date = Date.now(); + return { + ctime: date, + mtime: date, + size: libBuffer.length, + type: FileType.File, + }; + } + + readFile(uri: Uri): Uint8Array { + const libBuffer = this.getLibrary(uri); + return new Uint8Array(libBuffer); + } + + /** + * Fetches the library if it exists. + * Otherwise, throws a FileSystemError.FileNotFound. + * @returns the library content as Buffer + */ + private getLibrary(uri: Uri) { + const libBuffer = this.libraries.get(uri.toString()); + if (libBuffer === undefined) { + throw FileSystemError.FileNotFound(uri); + } + return libBuffer; + } + + watch() { + return { + // eslint-disable-next-line @typescript-eslint/no-empty-function + dispose: () => {}, + }; + } + + readDirectory(): [] { + throw FileSystemError.NoPermissions(); + } + + createDirectory() { + throw FileSystemError.NoPermissions(); + } + + writeFile() { + throw FileSystemError.NoPermissions(); + } + + delete() { + throw FileSystemError.NoPermissions(); + } + + rename() { + throw FileSystemError.NoPermissions(); + } +} diff --git a/apps/vs-code-extension/tsconfig.app.json b/apps/vs-code-extension/tsconfig.app.json new file mode 100644 index 00000000..402ff358 --- /dev/null +++ b/apps/vs-code-extension/tsconfig.app.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "types": ["node"] + }, + "exclude": ["**/*.spec.ts", "**/*.test.ts"], + "include": ["**/*.ts"] +} diff --git a/apps/vs-code-extension/tsconfig.app.json.license b/apps/vs-code-extension/tsconfig.app.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/vs-code-extension/tsconfig.app.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/apps/vs-code-extension/tsconfig.json b/apps/vs-code-extension/tsconfig.json new file mode 100644 index 00000000..816e3f36 --- /dev/null +++ b/apps/vs-code-extension/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.base.json", + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.app.json" + } + ] +} diff --git a/apps/vs-code-extension/tsconfig.json.license b/apps/vs-code-extension/tsconfig.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/apps/vs-code-extension/tsconfig.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/assets/docs-banner.png b/assets/docs-banner.png new file mode 100644 index 00000000..930a654b Binary files /dev/null and b/assets/docs-banner.png differ diff --git a/assets/docs-banner.png.license b/assets/docs-banner.png.license new file mode 100644 index 00000000..ee78e353 --- /dev/null +++ b/assets/docs-banner.png.license @@ -0,0 +1,7 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only + +Info: This image is based on a generated image by Midjourney (Basic subscription). +Midjourney ToS: https://docs.midjourney.com/docs/terms-of-service or archived on https://web.archive.org/web/20240205060308/https://docs.midjourney.com/docs/terms-of-service. +Date of creation: 2024-02-06 diff --git a/assets/mascot1.original.png b/assets/mascot1.original.png new file mode 100644 index 00000000..6ffa8d1b Binary files /dev/null and b/assets/mascot1.original.png differ diff --git a/assets/mascot1.original.png.license b/assets/mascot1.original.png.license new file mode 100644 index 00000000..ee78e353 --- /dev/null +++ b/assets/mascot1.original.png.license @@ -0,0 +1,7 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only + +Info: This image is based on a generated image by Midjourney (Basic subscription). +Midjourney ToS: https://docs.midjourney.com/docs/terms-of-service or archived on https://web.archive.org/web/20240205060308/https://docs.midjourney.com/docs/terms-of-service. +Date of creation: 2024-02-06 diff --git a/assets/mascot1.png b/assets/mascot1.png new file mode 100644 index 00000000..6d1d780d Binary files /dev/null and b/assets/mascot1.png differ diff --git a/assets/mascot1.png.license b/assets/mascot1.png.license new file mode 100644 index 00000000..ee78e353 --- /dev/null +++ b/assets/mascot1.png.license @@ -0,0 +1,7 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only + +Info: This image is based on a generated image by Midjourney (Basic subscription). +Midjourney ToS: https://docs.midjourney.com/docs/terms-of-service or archived on https://web.archive.org/web/20240205060308/https://docs.midjourney.com/docs/terms-of-service. +Date of creation: 2024-02-06 diff --git a/assets/mascot2.original.png b/assets/mascot2.original.png new file mode 100644 index 00000000..18cff494 Binary files /dev/null and b/assets/mascot2.original.png differ diff --git a/assets/mascot2.original.png.license b/assets/mascot2.original.png.license new file mode 100644 index 00000000..ee78e353 --- /dev/null +++ b/assets/mascot2.original.png.license @@ -0,0 +1,7 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only + +Info: This image is based on a generated image by Midjourney (Basic subscription). +Midjourney ToS: https://docs.midjourney.com/docs/terms-of-service or archived on https://web.archive.org/web/20240205060308/https://docs.midjourney.com/docs/terms-of-service. +Date of creation: 2024-02-06 diff --git a/assets/mascot2.png b/assets/mascot2.png new file mode 100644 index 00000000..b7b9adf1 Binary files /dev/null and b/assets/mascot2.png differ diff --git a/assets/mascot2.png.license b/assets/mascot2.png.license new file mode 100644 index 00000000..ee78e353 --- /dev/null +++ b/assets/mascot2.png.license @@ -0,0 +1,7 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only + +Info: This image is based on a generated image by Midjourney (Basic subscription). +Midjourney ToS: https://docs.midjourney.com/docs/terms-of-service or archived on https://web.archive.org/web/20240205060308/https://docs.midjourney.com/docs/terms-of-service. +Date of creation: 2024-02-06 diff --git a/assets/mascot3.original.png b/assets/mascot3.original.png new file mode 100644 index 00000000..04aceb20 Binary files /dev/null and b/assets/mascot3.original.png differ diff --git a/assets/mascot3.original.png.license b/assets/mascot3.original.png.license new file mode 100644 index 00000000..ee78e353 --- /dev/null +++ b/assets/mascot3.original.png.license @@ -0,0 +1,7 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only + +Info: This image is based on a generated image by Midjourney (Basic subscription). +Midjourney ToS: https://docs.midjourney.com/docs/terms-of-service or archived on https://web.archive.org/web/20240205060308/https://docs.midjourney.com/docs/terms-of-service. +Date of creation: 2024-02-06 diff --git a/assets/mascot3.png b/assets/mascot3.png new file mode 100644 index 00000000..9c0d1040 Binary files /dev/null and b/assets/mascot3.png differ diff --git a/assets/mascot3.png.license b/assets/mascot3.png.license new file mode 100644 index 00000000..ee78e353 --- /dev/null +++ b/assets/mascot3.png.license @@ -0,0 +1,7 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only + +Info: This image is based on a generated image by Midjourney (Basic subscription). +Midjourney ToS: https://docs.midjourney.com/docs/terms-of-service or archived on https://web.archive.org/web/20240205060308/https://docs.midjourney.com/docs/terms-of-service. +Date of creation: 2024-02-06 diff --git a/babel.config.json b/babel.config.json new file mode 100644 index 00000000..065aee77 --- /dev/null +++ b/babel.config.json @@ -0,0 +1,3 @@ +{ + "babelrcRoots": ["*"] +} diff --git a/babel.config.json.license b/babel.config.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/babel.config.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/example/cars.jv b/example/cars.jv new file mode 100644 index 00000000..416b2da2 --- /dev/null +++ b/example/cars.jv @@ -0,0 +1,108 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// Example 1: Cars +// Learning goals: +// - Understand the core concepts pipeline, block, and pipe +// - Understand the general structure of a pipeline + +// 1. This Jayvee model describes a pipeline +// from a CSV file in the web +// to a SQLite file sink. +pipeline CarsPipeline { + + // 2. We describe the structure of the pipeline, + // usually at the top of the pipeline. + // by connecting blocks via pipes. + + // 3. Syntax of a pipe + // connecting the block CarsExtractor + // with the block CarsTextFileInterpreter. + CarsExtractor + -> CarsTextFileInterpreter; + + // 4. The output of the preceding block is hereby used + // as input for the succeeding block. + + // 5. Pipes can be further chained, + // leading to an overview of the pipeline. + CarsTextFileInterpreter + -> CarsCSVInterpreter + -> NameHeaderWriter + -> CarsTableInterpreter + -> CarsLoader; + + + // 6. Below the pipes, we usually define the blocks + // that are connected by the pipes. + + // 7. Blocks instantiate a block type by using the oftype keyword. + // The block type defines the available properties that the block + // can use to specify the intended behavior of the block + block CarsExtractor oftype HttpExtractor { + + // 8. Properties are assigned to concrete values. + // Here, we specify the URL where the file shall be downloaded from. + url: "https://gist.githubusercontent.com/noamross/e5d3e859aa0c794be10b/raw/b999fb4425b54c63cab088c0ce2c0d6ce961a563/cars.csv"; + } + + // 9. The HttpExtractor requires no input and produces a binary file as output. + // This file has to be interpreted, e.g., as text file. + block CarsTextFileInterpreter oftype TextFileInterpreter { } + + // 10. Next, we interpret the text file as sheet. + // A sheet only contains text cells and is useful for manipulating the shape of data before assigning more strict value types to cells. + block CarsCSVInterpreter oftype CSVInterpreter { + enclosing: '"'; + } + + // 11. We can write into cells of a sheet using the CellWriter block type. + block NameHeaderWriter oftype CellWriter { + // 12. We utilize a syntax similar to spreadsheet programs. + // Cell ranges can be described using the keywords "cell", "row", "column", or "range" that indicate which + // cells are selected for the write action. + at: cell A1; + + // 13. For each cell we selected with the "at" property above, + // we can specify what value shall be written into the cell. + write: [ + "name" + ]; + } + + // 14. As a next step, we interpret the sheet as a table by adding structure. + // We define a value type per column that specifies the data type of the column. + // Rows that include values that are not valid according to the their value types are dropped automatically. + block CarsTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "name" oftype text, + "mpg" oftype decimal, + "cyl" oftype integer, + "disp" oftype decimal, + "hp" oftype integer, + "drat" oftype decimal, + "wt" oftype decimal, + "qsec" oftype decimal, + "vs" oftype integer, + "am" oftype integer, + "gear" oftype integer, + "carb" oftype integer + ]; + } + + // 15. As a last step, we load the table into a sink, + // here into a sqlite file. + // The structural information of the table is used + // to generate the correct table. + block CarsLoader oftype SQLiteLoader { + table: "Cars"; + file: "./cars.sqlite"; + } + + // 16. Congratulations! + // You can now use the sink for your data analysis, app, + // or whatever you want to do with the cleaned data. +} + diff --git a/example/docker-compose.example.yml b/example/docker-compose.example.yml new file mode 100644 index 00000000..9c591bc5 --- /dev/null +++ b/example/docker-compose.example.yml @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +# +# SPDX-License-Identifier: AGPL-3.0-only + +version: '3.1' + +services: + jayvee-postgres: + image: postgres + restart: always + environment: + POSTGRES_HOST_AUTH_METHOD: 'trust' # removes requirement for a password + POSTGRES_DB: 'jvalue' + ports: + - 5432:5432 diff --git a/example/electric-vehicles.jv b/example/electric-vehicles.jv new file mode 100644 index 00000000..6fae249d --- /dev/null +++ b/example/electric-vehicles.jv @@ -0,0 +1,193 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// Example 2: Electric Vehicles +// Learning goals: +// - Understand further core concepts transforms and value types +// - Understand how to construct a pipeline with multiple sinks +// - Understand the use of runtime parameters + +// 1. This Jayvee model describes a pipeline +// from a CSV file in the web +// to a SQLite file and a PostgreSQL db sink. +pipeline ElectricVehiclesPipeline { + // See here for meta-data of the data source + // https://catalog.data.gov/dataset/electric-vehicle-population-data/resource/fa51be35-691f-45d2-9f3e-535877965e69 + + // 2. At the top of a pipeline, we describe the + // structure of the pipeline. The first part until + // the ElectricRangeTransformer is a linear sequence + // of blocks. From there we can see a split into two + // parallel sequences that load the data in to two + // different sinks. + ElectricVehiclesHttpExtractor + -> ElectricVehiclesTextFileInterpreter + -> ElectricVehiclesCSVInterpreter + -> ElectricVehiclesTableInterpreter + -> ElectricRangeTransformer; + + ElectricRangeTransformer + -> ElectricVehiclesSQLiteLoader; + + ElectricRangeTransformer + -> ElectricVehiclesPostgresLoader; + + // 3. After the pipeline structure, we define the blocks used. + block ElectricVehiclesHttpExtractor oftype HttpExtractor { + url: "https://data.wa.gov/api/views/f6w7-q2d2/rows.csv?accessType=DOWNLOAD"; + } + + block ElectricVehiclesTextFileInterpreter oftype TextFileInterpreter { } + + block ElectricVehiclesCSVInterpreter oftype CSVInterpreter { } + + block ElectricVehiclesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + // 4. Here, a user-deifned value type is used to describe this column. + // The capital letter indicates that the value type is not built-in + // by convention. The value type itself is defined further below. + "VIN (1-10)" oftype VehicleIdentificationNumber10, + "County" oftype text, + "City" oftype text, + "State" oftype UsStateCode, + "Postal Code" oftype text, + "Model Year" oftype integer, + "Make" oftype text, + "Model" oftype text, + "Electric Vehicle Type" oftype text, + "Clean Alternative Fuel Vehicle (CAFV) Eligibility" oftype text, + "Electric Range" oftype integer, + "Base MSRP" oftype integer, + "Legislative District" oftype text, + "DOL Vehicle ID" oftype integer, + "Vehicle Location" oftype text, + "Electric Utility" oftype text, + "2020 Census Tract" oftype text, + ]; + } + + // 5. This block describes the application of a transform function + // taking a column as input and adding another computed column. + // The applied transform function is defined below and referenced + // by the "use" property. + block ElectricRangeTransformer oftype TableTransformer { + inputColumns: [ + "Electric Range" + ]; + outputColumn: "Electric Range (km)"; + use: MilesToKilometers; + } + + // 6. Here, we define a transform function, taking parameters + // as input ("from" keyword), and producing an output ("to" keyword). + // Inputs and outputs have to be further described by a value type. + transform MilesToKilometers { + from miles oftype decimal; + to kilometers oftype integer; + + // 7. In order to express what the transform function does, + // we assign an expression to the output. Values from the input and output of the transform can be referred to by name. + kilometers: round (miles * 1.609344); + } + + block ElectricVehiclesSQLiteLoader oftype SQLiteLoader { + table: "ElectricVehiclePopulationData"; + file: "./electric-vehicles.sqlite"; + } + + block ElectricVehiclesPostgresLoader oftype PostgresLoader { + // 8. The requires keyword allows us to define runtime parameters. + // These values have to be provided as environment variables when interpreting the Jayvee model. + host: requires DB_HOST; + port: requires DB_PORT; + username: requires DB_USERNAME; + password: requires DB_PASSWORD; + database: requires DB_DATABASE; + table: "ElectricVehiclePopulationData"; + } +} + +// 9. Below the pipeline, we model user-define value types. +// We give them a speaking name and provide a base value type +// that this value type builts on. User-defined value types always place additional constraints on existing value types. +valuetype VehicleIdentificationNumber10 oftype text { + // 10. Value types can be further refined by providing constraints. + constraints: [ + OnlyCapitalLettersAndDigits, + ExactlyTenCharacters, + ]; +} + +// 11. This constraint works on text value types and requires values +// to match a given regular expression in order to be valid. +constraint OnlyCapitalLettersAndDigits on text: value matches /^[A-Z0-9]*$/; + +constraint ExactlyTenCharacters on text: value.length == 10; + +valuetype UsStateCode oftype text { + constraints: [ + UsStateCodeAllowlist, + ]; +} + +constraint UsStateCodeAllowlist on text: value in [ + "AL", + "AK", + "AZ", + "AR", + "AS", + "CA", + "CO", + "CT", + "DE", + "DC", + "FL", + "GA", + "GU", + "HI", + "ID", + "IL", + "IN", + "IA", + "KS", + "KY", + "LA", + "ME", + "MD", + "MA", + "MI", + "MN", + "MS", + "MO", + "MT", + "NE", + "NV", + "NH", + "NJ", + "NM", + "NY", + "NC", + "ND", + "MP", + "OH", + "OK", + "OR", + "PA", + "PR", + "RI", + "SC", + "SD", + "TN", + "TX", + "TT", + "UT", + "VT", + "VA", + "VI", + "WA", + "WV", + "WI", + "WY", +]; diff --git a/example/gtfs-rt.jv b/example/gtfs-rt.jv new file mode 100644 index 00000000..f1891d34 --- /dev/null +++ b/example/gtfs-rt.jv @@ -0,0 +1,127 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// Example 3: GTFS Realtime Data +// Learning goals: +// - Understand the construction of a csv file with multiple tables +// - Understand how to work with live data + +// 1. This Jayvee model describes a pipeline +// from a GTFS RT data source in the web +// to a SQLite file with multiple tables. +pipeline GtfsRTSimplePipeline { + + // 2. As you can see here, we have three independent + // sequences of pipes in this pipeline. + GTFSRTTripUpdateFeedExtractor + ->GtfsRTTripUpdateInterpreter + ->TripUpdateTableInterpreter + ->TripUpdateLoader; + + GTFSRTVehiclePositionFeedExtractor + ->GtfsRTVehiclePositionInterpreter + ->VehiclePositionTableInterpreter + ->VehicleLoader; + + GTFSRTAlertFeedExtractor + ->GtfsRTAlertInterpreter + ->AlertTableInterpreter + ->AlertLoader; + + // 3. We define a series of HttpExtractors that each pull data + // from an HTTP endpoint + block GTFSRTTripUpdateFeedExtractor oftype HttpExtractor { + url: "https://proxy.transport.data.gouv.fr/resource/bibus-brest-gtfs-rt-trip-update"; + } + + block GTFSRTVehiclePositionFeedExtractor oftype HttpExtractor { + url: "https://proxy.transport.data.gouv.fr/resource/bibus-brest-gtfs-rt-vehicle-position"; + } + + block GTFSRTAlertFeedExtractor oftype HttpExtractor { + url: "https://proxy.transport.data.gouv.fr/resource/bibus-brest-gtfs-rt-alerts"; + } + + // 4. In the next step, we use the domain-specific GtfsRTInterpreter + // to interpret the fetched files as sheets + block GtfsRTTripUpdateInterpreter oftype GtfsRTInterpreter { + entity: "trip_update"; + } + + block GtfsRTAlertInterpreter oftype GtfsRTInterpreter { + entity: "alert"; + } + + block GtfsRTVehiclePositionInterpreter oftype GtfsRTInterpreter { + entity: "vehicle"; + } + + // 5. Next, we interpret the sheets as tables + block TripUpdateTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "header.gtfs_realtime_version" oftype text, + "header.timestamp" oftype text, + "header.incrementality" oftype text, + "entity.id" oftype text, + "entity.trip_update.trip.trip_id" oftype text, + "entity.trip_update.trip.route_id" oftype text, + "entity.trip_update.stop_time_update.stop_sequence" oftype text, + "entity.trip_update.stop_time_update.stop_id" oftype text, + "entity.trip_update.stop_time_update.arrival.time" oftype text, + "entity.trip_update.stop_time_update.departure.time" oftype text, + ]; + } + + block VehiclePositionTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "header.gtfs_realtime_version" oftype text, + "header.timestamp" oftype text, + "header.incrementality" oftype text, + "entity.id" oftype text, + "entity.vehicle_position.vehicle_descriptor.id" oftype text, + "entity.vehicle_position.trip.trip_id" oftype text, + "entity.vehicle_position.trip.route_id" oftype text, + "entity.vehicle_position.position.latitude" oftype text, + "entity.vehicle_position.position.longitude" oftype text, + "entity.vehicle_position.timestamp" oftype text + ]; + } + + block AlertTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + 'header.gtfs_realtime_version' oftype text, + 'header.timestamp' oftype text, + 'header.incrementality' oftype text, + 'entity.id' oftype text, + 'entity.alert.informed_entity.route_id' oftype text, + 'entity.alert.header_text' oftype text, + 'entity.alert.description_text' oftype text, + ]; + } + + // 6. Last, we load the tables into the same SQLite file. + // Each loader has to define a different table name. + // For working with live data, we use the property "dropTable: false" + // to append data instead of deleting the previous data. + block TripUpdateLoader oftype SQLiteLoader { + table: "gtfs-rt-trip_update"; + file: "./gtfs.sqlite"; + dropTable: false; + } + + block VehicleLoader oftype SQLiteLoader { + table: "gtfs-rt-vehicle_position"; + file: "./gtfs.sqlite"; + dropTable: false; + } + + block AlertLoader oftype SQLiteLoader { + table: "gtfs-rt-alert"; + file: "./gtfs.sqlite"; + dropTable: false; + } +} \ No newline at end of file diff --git a/example/gtfs-static.jv b/example/gtfs-static.jv new file mode 100644 index 00000000..ec0ef003 --- /dev/null +++ b/example/gtfs-static.jv @@ -0,0 +1,125 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// Example 4: GTFS Static Data +// Learning goals: +// - Understand how to work with file systems + +// 1. This Jayvee model describes a pipeline +// from a zip file in the GTFS format in the web +// to a joint SQLite file with multiple tables. +pipeline GtfsPipeline { + + // 2. The origin for multiple pipe sequences is a zip + // file. Each file in this zip is further processed + // by its own sequence of blocks and pipes. + GTFSSampleFeedExtractor + -> AgencyInterpreter + -> AgencyLoader; + + GTFSSampleFeedExtractor + -> CalendarDatesInterpreter + -> CalendarDatesLoader; + + GTFSSampleFeedExtractor + -> CalendarInterpreter + -> CalendarLoader; + + GTFSSampleFeedExtractor + -> FareAttributesInterpreter + -> FareAttributesLoader; + + GTFSSampleFeedExtractor + -> FareRulesInterpreter + -> FareRulesLoader; + + GTFSSampleFeedExtractor + -> FrequenciesInterpreter + -> FrequenciesLoader; + + GTFSSampleFeedExtractor + -> RoutesInterpreter + -> RoutesLoader; + + GTFSSampleFeedExtractor + -> ShapesInterpreter + -> ShapesLoader; + + GTFSSampleFeedExtractor + -> StopTimesInterpreter + -> StopTimesLoader; + + GTFSSampleFeedExtractor + -> StopsInterpreter + -> StopsLoader; + + GTFSSampleFeedExtractor + -> TripsInterpreter + -> TripsLoader; + + // 3. As a first step, we download the zip file and interpret it. + block GTFSSampleFeedExtractor oftype GTFSExtractor { + url: "https://developers.google.com/static/transit/gtfs/examples/sample-feed.zip"; + } + + // 4. Next, interpret the zip files contents according to the different elements + // from the GTFS standard. + block AgencyInterpreter oftype GTFSAgencyInterpreter { } + block CalendarDatesInterpreter oftype GTFSCalendarDatesInterpreter { } + block CalendarInterpreter oftype GTFSCalendarInterpreter { } + block FareAttributesInterpreter oftype GTFSFareAttributesInterpreter { } + block FareRulesInterpreter oftype GTFSFareRulesInterpreter { } + block FrequenciesInterpreter oftype GTFSFrequenciesInterpreter { } + block RoutesInterpreter oftype GTFSRoutesInterpreter { } + block ShapesInterpreter oftype GTFSShapesInterpreter { } + block StopTimesInterpreter oftype GTFSStopTimesInterpreter { } + block StopsInterpreter oftype GTFSStopsInterpreter { } + block TripsInterpreter oftype GTFSTripsInterpreter { } + + // 5. Finally, write the interpreted tables into a SQLite database + block AgencyLoader oftype SQLiteLoader { + table: "agency"; + file: "./gtfs.sqlite"; + } + block CalendarDatesLoader oftype SQLiteLoader { + table: "calendar_dates"; + file: "./gtfs.sqlite"; + } + block CalendarLoader oftype SQLiteLoader { + table: "calendar"; + file: "./gtfs.sqlite"; + } + block FareAttributesLoader oftype SQLiteLoader { + table: "fare_attributes"; + file: "./gtfs.sqlite"; + } + block FareRulesLoader oftype SQLiteLoader { + table: "fare_rules"; + file: "./gtfs.sqlite"; + } + block FrequenciesLoader oftype SQLiteLoader { + table: "frequencies"; + file: "./gtfs.sqlite"; + } + block RoutesLoader oftype SQLiteLoader { + table: "routes"; + file: "./gtfs.sqlite"; + } + block ShapesLoader oftype SQLiteLoader { + table: "shapes"; + file: "./gtfs.sqlite"; + } + block StopTimesLoader oftype SQLiteLoader { + table: "stop_times"; + file: "./gtfs.sqlite"; + } + block StopsLoader oftype SQLiteLoader { + table: "stops"; + file: "./gtfs.sqlite"; + } + block TripsLoader oftype SQLiteLoader { + table: "trips"; + file: "./gtfs.sqlite"; + } +} \ No newline at end of file diff --git a/example/one-block.jv b/example/one-block.jv new file mode 100644 index 00000000..3e98370f --- /dev/null +++ b/example/one-block.jv @@ -0,0 +1,119 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// Example 1: Cars +// Learning goals: +// - Understand the core concepts pipeline, block, and pipe +// - Understand the general structure of a pipeline + +// 1. This Jayvee model describes a pipeline +// from a CSV file in the web +// to a SQLite file sink. +pipeline CarsPipeline { + + // 2. We describe the structure of the pipeline, + // usually at the top of the pipeline. + // by connecting blocks via pipes. + + // 3. Syntax of a pipe + // connecting the block CarsExtractor + // with the block CarsTextFileInterpreter. + CarsExtractor -> CarsInterpreter; + + // 4. The output of the preceding block is hereby used + // as input for the succeeding block. + + // 5. Pipes can be further chained, + // leading to an overview of the pipeline. + CarsInterpreter + -> CarsLoader; + + + // 6. Below the pipes, we usually define the blocks + // that are connected by the pipes. + + // 7. Blocks instantiate a block type by using the oftype keyword. + // The block type defines the available properties that the block + // can use to specify the intended behavior of the block + block CarsExtractor oftype HttpExtractor { + + // 8. Properties are assigned to concrete values. + // Here, we specify the URL where the file shall be downloaded from. + url: "https://gist.githubusercontent.com/noamross/e5d3e859aa0c794be10b/raw/b999fb4425b54c63cab088c0ce2c0d6ce961a563/cars.csv"; + } + + block CarsInterpreter oftype FileToTableInterpreter { + header: true; + columns: [ + "name" oftype decimal, + "mpg" oftype decimal, + "cyl" oftype integer, + "disp" oftype decimal, + "hp" oftype integer, + "drat" oftype decimal, + "wt" oftype decimal, + "qsec" oftype decimal, + "vs" oftype integer, + "am" oftype integer, + "gear" oftype integer, + "carb" oftype integer + ]; + } + + // 9. The HttpExtractor requires no input and produces a binary file as output. + // This file has to be interpreted, e.g., as text file. + block CarsTextFileInterpreter oftype TextFileInterpreter { } + + // 10. Next, we interpret the text file as sheet. + // A sheet only contains text cells and is useful for manipulating the shape of data before assigning more strict value types to cells. + block CarsCSVInterpreter oftype CSVInterpreter { + enclosing: '"'; + } + + // 11. We can write into cells of a sheet using the CellWriter block type. + block NameHeaderWriter oftype CellWriter { + // 12. We utilize a syntax similar to spreadsheet programs. + // Cell ranges can be described using the keywords "cell", "row", "column", or "range" that indicate which + // cells are selected for the write action. + at: cell A1; + + // 13. For each cell we selected with the "at" property above, + // we can specify what value shall be written into the cell. + write: ["name"]; + } + + // 14. As a next step, we interpret the sheet as a table by adding structure. + // We define a value type per column that specifies the data type of the column. + // Rows that include values that are not valid according to the their value types are dropped automatically. + block CarsTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "name" oftype text, + "mpg" oftype decimal, + "cyl" oftype integer, + "disp" oftype decimal, + "hp" oftype integer, + "drat" oftype decimal, + "wt" oftype decimal, + "qsec" oftype decimal, + "vs" oftype integer, + "am" oftype integer, + "gear" oftype integer, + "carb" oftype integer + ]; + } + + // 15. As a last step, we load the table into a sink, + // here into a sqlite file. + // The structural information of the table is used + // to generate the correct table. + block CarsLoader oftype SQLiteLoader { + table: "Cars"; + file: "./cars.sqlite"; + } + + // 16. Congratulations! + // You can now use the sink for your data analysis, app, + // or whatever you want to do with the cleaned data. +} diff --git a/example/testing.jv b/example/testing.jv new file mode 100644 index 00000000..9e4ec089 --- /dev/null +++ b/example/testing.jv @@ -0,0 +1,69 @@ +pipeline CarsPipeline { + + CarsExtractor + -> CarsTextFileInterpreter + -> CarsCSVInterpreter + -> NameHeaderWriter + -> CarsTableInterpreter + -> MyTableTransformer + -> CarsLoader; + + + block CarsExtractor oftype HttpExtractor { + url: "https://gist.githubusercontent.com/noamross/e5d3e859aa0c794be10b/raw/b999fb4425b54c63cab088c0ce2c0d6ce961a563/cars.csv"; + } + + + block CarsTextFileInterpreter oftype TextFileInterpreter { } + + block CarsCSVInterpreter oftype CSVInterpreter { + enclosing: '"'; + } + + block NameHeaderWriter oftype CellWriter { + at: cell A1; + + write: [ + "name" + ]; + } + + block CarsTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "name" oftype text, + "mpg" oftype decimal, + "cyl" oftype integer, + "disp" oftype decimal, + "hp" oftype integer, + "drat" oftype decimal, + "wt" oftype decimal, + "qsec" oftype decimal, + "vs" oftype integer, + "am" oftype integer, + "gear" oftype integer, + "carb" oftype integer + ]; + } + + transform IdentityTransform { + from mpg oftype decimal; + from cyl oftype integer; + to o oftype decimal; + + o: mpg + cyl; + } + + block MyTableTransformer oftype TableTransformer { + inputColumns: ['mpg', 'cyl']; + outputColumn: 'f(mpg, cyl)'; + + use: IdentityTransform; + } + + block CarsLoader oftype SQLiteLoader { + table: "Cars"; + file: "./cars.sqlite"; + } +} + diff --git a/example/workbooks-xlsx.jv b/example/workbooks-xlsx.jv new file mode 100644 index 00000000..a50b5b08 --- /dev/null +++ b/example/workbooks-xlsx.jv @@ -0,0 +1,97 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// Example 1: LightTrapping +// Learning goals: +// - Understand how to work with XLSX files and workbooks + +// 1. This Jayvee model describes a pipeline +// from a XLSX file with multiple Sheets in the web +// to a SQLite file sink. +pipeline LightTrappingSiliconSolarCellsPipeline { + // 2. We directly get the xlsx file from the web via the HttpExtractor + // The data is provided under CC BY-SA 4.0 + // Saive, Rebecca (2023). Data supporting the publication: + // Light trapping in thin silicon solar cells: a review on fundamentals and technologies. + // 4TU.ResearchData. Dataset. https://doi.org/10.4121/14554815.v1 + block LightTrappingSiliconSolarCellsExtractor oftype HttpExtractor { + url: "https://figshare.com/ndownloader/files/27923598"; + } + + // 3. The incoming file is interpreted as a XLSX file and transformed into a Workbook + // Workbooks contain at least 1 Sheet. Every sheet has a unique name. + block LightTrappingSiliconSolarCellsTextXLSXInterpreter oftype XLSXInterpreter { } + + // 4.1 Here, we pick one sheet with the name 'RefractiveIndexSi GaAs' from the Workbook to use within our pipeline. + // The output type from SheetPicker is Sheet, which was already introduced in the cars example + block LightTrappingSiliconSolarCellsSheetpicker oftype SheetPicker { + sheetName: 'RefractiveIndexSi GaAs'; + } + + block NameHeaderWriter oftype CellWriter { + at: range F1:L1; + write: [ + "F", + "G", + "nm", + "wl", + "n2", + "k2", + "alpha (cm-1)2" + ]; + } + + block LightTrappingSiliconSolarCellsTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "Wavelength" oftype integer, + "Wavelength (µm)" oftype decimal, + "n" oftype decimal, + "k" oftype text, + "alpha (cm-1)" oftype text, + "nm" oftype decimal, + "n2" oftype text, + "k2" oftype decimal, + "alpha (cm-1)2" oftype decimal + ]; + } + + block LightTrappingSiliconSolarCellsLoader oftype SQLiteLoader { + table: "LightTrappingSiliconSolarCells"; + file: "./LightTrappingSiliconSolarCells.sqlite"; + } + + // 4.2 Here, we pick another sheet named 'Wavelength thickness trapping' from the Workbook + block SecondLightTrappingSiliconSolarCellsSheetpicker oftype SheetPicker { + sheetName: 'Wavelength thickness trapping'; + } + + block SecondLightTrappingSiliconSolarCellsTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "n" oftype decimal, + "Wavelength (µm)" oftype decimal, + ]; + } + + block SecondLightTrappingSiliconSolarCellsLoader oftype SQLiteLoader { + + table: "SecondLightTrappingSiliconSolarCells"; + file: "./LightTrappingSiliconSolarCells.sqlite"; + } + + LightTrappingSiliconSolarCellsExtractor + -> LightTrappingSiliconSolarCellsTextXLSXInterpreter + -> LightTrappingSiliconSolarCellsSheetpicker + -> NameHeaderWriter + -> LightTrappingSiliconSolarCellsTableInterpreter + -> LightTrappingSiliconSolarCellsLoader; + + // 5. Once the XLSX file is interpreted, we can split the pipeline and + // work separately on the different sheets from our input file + LightTrappingSiliconSolarCellsTextXLSXInterpreter + -> SecondLightTrappingSiliconSolarCellsSheetpicker + -> SecondLightTrappingSiliconSolarCellsTableInterpreter + -> SecondLightTrappingSiliconSolarCellsLoader; +} \ No newline at end of file diff --git a/libs/execution/.babelrc b/libs/execution/.babelrc new file mode 100644 index 00000000..fd4cbcde --- /dev/null +++ b/libs/execution/.babelrc @@ -0,0 +1,10 @@ +{ + "presets": [ + [ + "@nx/js/babel", + { + "useBuiltIns": "usage" + } + ] + ] +} diff --git a/libs/execution/.babelrc.license b/libs/execution/.babelrc.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/execution/.babelrc.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/execution/.eslintrc.json b/libs/execution/.eslintrc.json new file mode 100644 index 00000000..eb4abff2 --- /dev/null +++ b/libs/execution/.eslintrc.json @@ -0,0 +1,21 @@ +{ + "extends": ["../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "parserOptions": { + "project": ["libs/execution/tsconfig.lib.json", "libs/execution/tsconfig.spec.json"] + }, + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/libs/execution/.eslintrc.json.license b/libs/execution/.eslintrc.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/execution/.eslintrc.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/execution/README.md b/libs/execution/README.md new file mode 100644 index 00000000..5498dbc4 --- /dev/null +++ b/libs/execution/README.md @@ -0,0 +1,17 @@ +<!-- +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only +--> + +# execution + +This library was generated with [Nx](https://nx.dev). + +## Building + +Run `nx build execution` to build the library. + +## Running unit tests + +Run `nx test execution` to execute the unit tests via [vitest](https://vitest.dev). diff --git a/libs/execution/package.json b/libs/execution/package.json new file mode 100644 index 00000000..0ba36b4b --- /dev/null +++ b/libs/execution/package.json @@ -0,0 +1,3 @@ +{ + "name": "@jvalue/jayvee-execution" +} diff --git a/libs/execution/package.json.license b/libs/execution/package.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/execution/package.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/execution/project.json b/libs/execution/project.json new file mode 100644 index 00000000..5bd7e614 --- /dev/null +++ b/libs/execution/project.json @@ -0,0 +1,22 @@ +{ + "name": "execution", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/execution/src", + "projectType": "library", + "targets": { + "build": {}, + "lint": { + "executor": "@nx/eslint:lint", + "outputs": ["{options.outputFile}"] + }, + "test": { + "executor": "@nx/vite:test", + "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], + "options": { + "configFile": "{projectRoot}/vite.config.ts", + "passWithNoTests": false + } + } + }, + "tags": [] +} diff --git a/libs/execution/project.json.license b/libs/execution/project.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/execution/project.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/execution/src/index.ts b/libs/execution/src/index.ts new file mode 100644 index 00000000..ec01a8e2 --- /dev/null +++ b/libs/execution/src/index.ts @@ -0,0 +1,5 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +export * from './lib'; diff --git a/libs/execution/src/lib/blocks/block-execution-util.ts b/libs/execution/src/lib/blocks/block-execution-util.ts new file mode 100644 index 00000000..7f741adc --- /dev/null +++ b/libs/execution/src/lib/blocks/block-execution-util.ts @@ -0,0 +1,128 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type BlockDefinition, + type CompositeBlockTypeDefinition, + type PipelineDefinition, +} from '@jvalue/jayvee-language-server'; + +import { type ExecutionContext } from '../execution-context'; +import { type Logger } from '../logging/logger'; +import { type IOTypeImplementation, NONE } from '../types'; + +import * as R from './execution-result'; + +export interface ExecutionOrderItem { + block: BlockDefinition; + value: IOTypeImplementation | null; +} + +/** + * Executes an ordered list of blocks in sequence, using outputs from previous blocks as inputs for downstream blocks. + * + * @param executionContext The context the blocks are executed in, e.g., a pipeline or composite block + * @param executionOrder An ordered list of blocks so that blocks that need inputs are after blocks that produce these inputs + * @param initialInputValue An initial input that was produced outside of this block chain, e.g., as input to a composite block + * + * @returns The ordered blocks and their produced outputs or an error on failure + */ +export async function executeBlocks( + executionContext: ExecutionContext, + pipesContainer: CompositeBlockTypeDefinition | PipelineDefinition, + initialInputValue: IOTypeImplementation | undefined = undefined, +): Promise<R.Result<ExecutionOrderItem[]>> { + const pipelineWrapper = + executionContext.wrapperFactories.Pipeline.wrap(pipesContainer); + const executionOrder: { + block: BlockDefinition; + value: IOTypeImplementation | null; + }[] = pipelineWrapper.getBlocksInTopologicalSorting().map((block) => { + return { block: block, value: NONE }; + }); + + let isFirstBlock = true; + + for (const blockData of executionOrder) { + const block = blockData.block; + const parentData = pipelineWrapper + .getParentBlocks(block) + .map((parent) => + executionOrder.find((blockData) => parent === blockData.block), + ); + let inputValue = parentData[0]?.value ?? NONE; + + const useExternalInputValueForFirstBlock = + isFirstBlock && inputValue === NONE && initialInputValue !== undefined; + + if (useExternalInputValueForFirstBlock) { + inputValue = initialInputValue; + } + + executionContext.enterNode(block); + + const executionResult = await executeBlock( + inputValue, + block, + executionContext, + ); + if (R.isErr(executionResult)) { + return executionResult; + } + const blockResultData = executionResult.right; + blockData.value = blockResultData; + + executionContext.exitNode(block); + isFirstBlock = false; + } + return R.ok(executionOrder); +} + +export async function executeBlock( + inputValue: IOTypeImplementation | null, + block: BlockDefinition, + executionContext: ExecutionContext, +): Promise<R.Result<IOTypeImplementation | null>> { + if (inputValue == null) { + executionContext.logger.logInfoDiagnostic( + `Skipped execution because parent block emitted no value.`, + { node: block, property: 'name' }, + ); + return R.ok(null); + } + + const blockExecutor = executionContext.executionExtension.createBlockExecutor( + block, + executionContext.runOptions.usePolars, + executionContext.logger, + ); + + const startTime = new Date(); + + let result: R.Result<IOTypeImplementation | null>; + try { + result = await blockExecutor.execute(inputValue, executionContext); + } catch (unexpectedError) { + return R.err({ + message: `An unknown error occurred: ${ + unexpectedError instanceof Error + ? unexpectedError.stack ?? unexpectedError.message + : JSON.stringify(unexpectedError) + }`, + diagnostic: { node: block, property: 'name' }, + }); + } + + logExecutionDuration(startTime, executionContext.logger); + + return result; +} + +export function logExecutionDuration(startTime: Date, logger: Logger): void { + const endTime = new Date(); + const executionDurationMs = Math.round( + endTime.getTime() - startTime.getTime(), + ); + logger.logDebug(`Execution duration: ${executionDurationMs} ms.`); +} diff --git a/libs/execution/src/lib/blocks/block-executor-class.ts b/libs/execution/src/lib/blocks/block-executor-class.ts new file mode 100644 index 00000000..c6605a52 --- /dev/null +++ b/libs/execution/src/lib/blocks/block-executor-class.ts @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type ConstructorClass } from '@jvalue/jayvee-language-server'; + +import { type BlockExecutor } from './block-executor'; + +export interface BlockExecutorClass<T extends BlockExecutor = BlockExecutor> + extends ConstructorClass<T> { + readonly type: string; +} diff --git a/libs/execution/src/lib/blocks/block-executor.ts b/libs/execution/src/lib/blocks/block-executor.ts new file mode 100644 index 00000000..c0126c22 --- /dev/null +++ b/libs/execution/src/lib/blocks/block-executor.ts @@ -0,0 +1,83 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; + +import { type IOType, isBlockDefinition } from '@jvalue/jayvee-language-server'; + +import { isBlockTargetedForDebugLogging } from '../debugging/debug-configuration'; +import { DebugLogVisitor } from '../debugging/debug-log-visitor'; +import { type ExecutionContext } from '../execution-context'; +import { type IOTypeImplementation } from '../types/io-types/io-type-implementation'; + +import * as R from './execution-result'; + +export interface BlockExecutor< + I extends IOType = IOType, + O extends IOType = IOType, +> { + readonly inputType: I; + readonly outputType: O; + + execute( + input: IOTypeImplementation<I>, + context: ExecutionContext, + ): Promise<R.Result<IOTypeImplementation<O> | null>>; +} + +export abstract class AbstractBlockExecutor<I extends IOType, O extends IOType> + implements BlockExecutor<I, O> +{ + constructor(public readonly inputType: I, public readonly outputType: O) {} + + async execute( + input: IOTypeImplementation<I>, + context: ExecutionContext, + ): Promise<R.Result<IOTypeImplementation<O> | null>> { + const executionResult = await this.doExecute(input, context); + + if (R.isOk(executionResult)) { + this.logBlockResult(executionResult.right, context); + } + return executionResult; + } + + private logBlockResult( + result: IOTypeImplementation | null, + context: ExecutionContext, + ): void { + if (!context.runOptions.isDebugMode) { + return; + } + + if (result == null) { + return; + } + + const currentNode = context.getCurrentNode(); + assert(isBlockDefinition(currentNode)); + const isBlockTargeted = isBlockTargetedForDebugLogging( + currentNode, + context, + ); + if (!isBlockTargeted) { + return; + } + + result.acceptVisitor( + new DebugLogVisitor( + context.runOptions.debugGranularity, + 'Output', + context.logger, + context.wrapperFactories, + ), + ); + } + + abstract doExecute( + input: IOTypeImplementation<I>, + context: ExecutionContext, + ): Promise<R.Result<IOTypeImplementation<O> | null>>; +} diff --git a/libs/execution/src/lib/blocks/composite-block-executor.ts b/libs/execution/src/lib/blocks/composite-block-executor.ts new file mode 100644 index 00000000..9acb5ddc --- /dev/null +++ b/libs/execution/src/lib/blocks/composite-block-executor.ts @@ -0,0 +1,218 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; + +import { + type BlockDefinition, + type BlockTypePipeline, + type BlockTypeProperty, + type CompositeBlockTypeDefinition, + type EvaluationContext, + IOType, + type InternalValueRepresentation, + type ValueType, + type WrapperFactoryProvider, + evaluateExpression, + evaluatePropertyValue, + getIOType, + isCompositeBlockTypeDefinition, +} from '@jvalue/jayvee-language-server'; + +import { type ExecutionContext } from '../execution-context'; +import { type IOTypeImplementation } from '../types'; + +import { executeBlocks } from './block-execution-util'; +import { AbstractBlockExecutor, type BlockExecutor } from './block-executor'; +import { type BlockExecutorClass } from './block-executor-class'; +import * as R from './execution-result'; + +export function createCompositeBlockExecutor( + inputType: IOType, + outputType: IOType, + block: BlockDefinition, +): BlockExecutorClass<BlockExecutor<IOType, IOType>> { + assert( + block.type.ref, + `Reference to its type is missing for block ${block.name}.`, + ); + + assert( + isCompositeBlockTypeDefinition(block.type.ref), + `Type is not a composite block type for block ${block.name}.`, + ); + + const blockTypeReference = block.type.ref; + + return class extends AbstractBlockExecutor< + typeof inputType, + typeof outputType + > { + public static readonly type = blockTypeReference.name; + + constructor() { + super(inputType, outputType); + } + + async doExecute( + input: IOTypeImplementation<typeof inputType>, + context: ExecutionContext, + ): Promise<R.Result<IOTypeImplementation<typeof outputType> | null>> { + context.logger.logDebug( + `Executing composite block of type ${ + block.type.ref?.name ?? 'undefined' + }`, + ); + + this.addVariablesToContext(block, blockTypeReference.properties, context); + + const executionResult = await executeBlocks( + context, + blockTypeReference, + input, + ); + + if (R.isErr(executionResult)) { + const diagnosticError = executionResult.left; + context.logger.logErrDiagnostic( + diagnosticError.message, + diagnosticError.diagnostic, + ); + } + + this.removeVariablesFromContext(blockTypeReference.properties, context); + + if (R.isOk(executionResult)) { + // The last block always pipes into the output if it exists + const pipeline = getPipeline(blockTypeReference); + const lastBlock = pipeline.blocks.at(-1); + + const blockExecutionResult = R.okData(executionResult).find( + (result) => result.block.name === lastBlock?.ref?.name, + ); + + assert( + blockExecutionResult, + `No execution result found for composite block ${ + block.type.ref?.name ?? 'undefined' + }`, + ); + + return R.ok(blockExecutionResult.value); + } + + return R.ok(null); + } + + private removeVariablesFromContext( + properties: BlockTypeProperty[], + context: ExecutionContext, + ) { + properties.forEach((prop) => + context.evaluationContext.deleteValueForReference(prop.name), + ); + } + + private addVariablesToContext( + block: BlockDefinition, + properties: BlockTypeProperty[], + context: ExecutionContext, + ) { + properties.forEach((blockTypeProperty) => { + const valueType = context.wrapperFactories.ValueType.wrap( + blockTypeProperty.valueType, + ); + + assert( + valueType, + `Can not create value type for block type property ${blockTypeProperty.name}`, + ); + + const propertyValue = this.getPropertyValueFromBlockOrDefault( + blockTypeProperty.name, + valueType, + block, + properties, + context.evaluationContext, + context.wrapperFactories, + ); + + assert( + propertyValue !== undefined, + `Can not get value for block type property ${blockTypeProperty.name}`, + ); + + context.evaluationContext.setValueForReference( + blockTypeProperty.name, + propertyValue, + ); + }); + } + + private getPropertyValueFromBlockOrDefault( + name: string, + valueType: ValueType, + block: BlockDefinition, + properties: BlockTypeProperty[], + evaluationContext: EvaluationContext, + wrapperFactories: WrapperFactoryProvider, + ): InternalValueRepresentation | undefined { + const propertyFromBlock = block.body.properties.find( + (property) => property.name === name, + ); + + if (propertyFromBlock !== undefined) { + const value = evaluatePropertyValue( + propertyFromBlock, + evaluationContext, + wrapperFactories, + valueType, + ); + + if (value !== undefined) { + return value; + } + } + + const propertyFromBlockType = properties.find( + (property) => property.name === name, + ); + + if (propertyFromBlockType?.defaultValue === undefined) { + return; + } + + return evaluateExpression( + propertyFromBlockType.defaultValue, + evaluationContext, + wrapperFactories, + ); + } + }; +} + +function getPipeline(block: CompositeBlockTypeDefinition): BlockTypePipeline { + assert( + block.pipes[0], + `Composite block ${block.name} must have exactly one pipeline.`, + ); + return block.pipes[0]; +} + +export function getInputType(block: CompositeBlockTypeDefinition): IOType { + assert( + block.inputs.length === 1, + `Composite block ${block.name} must have exactly one input.`, + ); + return block.inputs[0] ? getIOType(block.inputs[0]) : IOType.NONE; +} + +export function getOutputType(block: CompositeBlockTypeDefinition): IOType { + assert( + block.outputs.length === 1, + `Composite block ${block.name} must have exactly one output.`, + ); + return block.outputs[0] ? getIOType(block.outputs[0]) : IOType.NONE; +} diff --git a/libs/execution/src/lib/blocks/execution-result.ts b/libs/execution/src/lib/blocks/execution-result.ts new file mode 100644 index 00000000..265e3a14 --- /dev/null +++ b/libs/execution/src/lib/blocks/execution-result.ts @@ -0,0 +1,58 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { either as E } from 'fp-ts'; +import { type AstNode, type DiagnosticInfo } from 'langium'; + +export interface ExecutionErrorDetails<N extends AstNode = AstNode> { + message: string; + diagnostic: DiagnosticInfo<N>; +} + +/** + * Convenience interfaces and methods wrapping @see Either of fp-ts library. + * Left is the @see ExecutionErrorDetails + * Right is a generic T + */ + +export type Result<T> = E.Either<ExecutionErrorDetails, T>; +export type Err = E.Left<ExecutionErrorDetails>; +export type Ok<T> = E.Right<T>; + +/** + * Creates an @see Ok object from a data object typed T. + * @param data the data object + * @returns the created @see Ok object + */ +export function ok<T>(data: T): Result<T> { + return E.right(data); +} +/** + * Creates an @see Err object from a @see ExecutionErrorDetails object. + * @param details the @see ExecutionErrorDetails object + * @returns the created @see Err object + */ +export function err<T>(details: ExecutionErrorDetails): Result<T> { + return E.left(details); +} + +/** + * Type guard for @see Ok + */ +export function isOk<T>(result: Result<T>): result is Ok<T> { + return E.isRight(result); +} +/** + * Type guard for @see Err + */ +export function isErr<T>(result: Result<T>): result is Err { + return E.isLeft(result); +} + +/** + * Convenience method to get wrapped data of an @see Ok object. + */ +export function okData<T>(ok: Ok<T>): T { + return ok.right; +} diff --git a/libs/execution/src/lib/blocks/index.ts b/libs/execution/src/lib/blocks/index.ts new file mode 100644 index 00000000..614c184f --- /dev/null +++ b/libs/execution/src/lib/blocks/index.ts @@ -0,0 +1,8 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +export * from './block-executor'; +export * from './block-executor-class'; +export * from './execution-result'; +export * from './block-execution-util'; diff --git a/libs/execution/src/lib/constraints/constraint-executor-extension.ts b/libs/execution/src/lib/constraints/constraint-executor-extension.ts new file mode 100644 index 00000000..e7fa2395 --- /dev/null +++ b/libs/execution/src/lib/constraints/constraint-executor-extension.ts @@ -0,0 +1,78 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; + +import { + type ConstraintDefinition, + Registry, + isExpressionConstraintDefinition, + isTypedConstraintDefinition, +} from '@jvalue/jayvee-language-server'; +import { assertUnreachable } from 'langium'; + +import { type ConstraintExecutor } from './constraint-executor'; +import { AllowlistConstraintExecutor } from './executors/allowlist-constraint-executor'; +import { DenylistConstraintExecutor } from './executors/denylist-constraint-executor'; +import { ExpressionConstraintExecutor } from './executors/expression-constraint-executor'; +import { LengthConstraintExecutor } from './executors/length-constraint-executor'; +import { RangeConstraintExecutor } from './executors/range-constraint-executor'; +import { RegexConstraintExecutor } from './executors/regex-constraint-executor'; +import { type TypedConstraintExecutorClass } from './typed-constraint-executor-class'; + +export interface JayveeConstraintExtension { + registerConstraintExecutor(executorClass: TypedConstraintExecutorClass): void; + + getConstraintExecutors(): TypedConstraintExecutorClass<ConstraintExecutor>[]; + + createConstraintExecutor( + constraint: ConstraintDefinition, + ): ConstraintExecutor; +} + +export class DefaultConstraintExtension + extends Registry<TypedConstraintExecutorClass> + implements JayveeConstraintExtension +{ + constructor() { + super(); + + this.registerConstraintExecutor(AllowlistConstraintExecutor); + this.registerConstraintExecutor(DenylistConstraintExecutor); + this.registerConstraintExecutor(RegexConstraintExecutor); + this.registerConstraintExecutor(LengthConstraintExecutor); + this.registerConstraintExecutor(RangeConstraintExecutor); + } + + registerConstraintExecutor(executorClass: TypedConstraintExecutorClass) { + this.register(executorClass.type, executorClass); + } + + getConstraintExecutors() { + return this.getAll(); + } + + createConstraintExecutor( + constraint: ConstraintDefinition, + ): ConstraintExecutor { + if (isTypedConstraintDefinition(constraint)) { + const constraintType = constraint.type.ref?.name; + assert( + constraintType !== undefined, + `Could not resolve reference to constraint type of ${constraint.name}`, + ); + const constraintExecutor = this.get(constraintType); + assert( + constraintExecutor !== undefined, + `No executor was registered for constraint type ${constraintType}`, + ); + + return new constraintExecutor(); + } else if (isExpressionConstraintDefinition(constraint)) { + return new ExpressionConstraintExecutor(constraint); + } + assertUnreachable(constraint); + } +} diff --git a/libs/execution/src/lib/constraints/constraint-executor.ts b/libs/execution/src/lib/constraints/constraint-executor.ts new file mode 100644 index 00000000..7dce1ed5 --- /dev/null +++ b/libs/execution/src/lib/constraints/constraint-executor.ts @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type InternalValueRepresentation } from '@jvalue/jayvee-language-server'; + +import { type ExecutionContext } from '../execution-context'; + +export interface ConstraintExecutor { + isValid( + value: InternalValueRepresentation, + context: ExecutionContext, + ): boolean; +} diff --git a/libs/execution/src/lib/constraints/default-constraint-executors-extension.spec.ts b/libs/execution/src/lib/constraints/default-constraint-executors-extension.spec.ts new file mode 100644 index 00000000..f3630ec3 --- /dev/null +++ b/libs/execution/src/lib/constraints/default-constraint-executors-extension.spec.ts @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + createJayveeServices, + getAllBuiltinConstraintTypes, + initializeWorkspace, +} from '@jvalue/jayvee-language-server'; +import { NodeFileSystem } from 'langium/node'; + +import { DefaultConstraintExtension } from './constraint-executor-extension'; + +describe('default constraint extension', () => { + it('should include executors for all constraint types', async () => { + // Create language services + const services = createJayveeServices(NodeFileSystem).Jayvee; + await initializeWorkspace(services); + + const defaultConstraintExtension = new DefaultConstraintExtension(); + + getAllBuiltinConstraintTypes( + services.shared.workspace.LangiumDocuments, + services.WrapperFactories, + ).forEach((constraintType) => { + const matchingConstraintExecutorClass = defaultConstraintExtension + .getConstraintExecutors() + .find((c) => c.type === constraintType.type); + + expect(matchingConstraintExecutorClass).toBeDefined(); + }); + }); +}); diff --git a/libs/execution/src/lib/constraints/executors/allowlist-constraint-executor.spec.ts b/libs/execution/src/lib/constraints/executors/allowlist-constraint-executor.spec.ts new file mode 100644 index 00000000..d623ff64 --- /dev/null +++ b/libs/execution/src/lib/constraints/executors/allowlist-constraint-executor.spec.ts @@ -0,0 +1,96 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type BlockDefinition, + type InternalValueRepresentation, + type JayveeServices, + type TypedConstraintDefinition, + createJayveeServices, +} from '@jvalue/jayvee-language-server'; +import { + type ParseHelperOptions, + expectNoParserAndLexerErrors, + parseHelper, + readJvTestAssetHelper, +} from '@jvalue/jayvee-language-server/test'; +import { + type AstNode, + type AstNodeLocator, + type LangiumDocument, +} from 'langium'; +import { NodeFileSystem } from 'langium/node'; + +import { getTestExecutionContext } from '../../../../test/utils'; + +import { AllowlistConstraintExecutor } from './allowlist-constraint-executor'; + +describe('Validation of AllowlistConstraintExecutor', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + let locator: AstNodeLocator; + let services: JayveeServices; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../../../test/assets/', + ); + + async function parseAndValidateConstraint( + input: string, + value: InternalValueRepresentation, + ): Promise<boolean> { + const document = await parse(input, { validation: true }); + expectNoParserAndLexerErrors(document); + + const usageBlock = locator.getAstNode<BlockDefinition>( + document.parseResult.value, + 'pipelines@0/blocks@2', + ) as BlockDefinition; + const constraint = locator.getAstNode<TypedConstraintDefinition>( + document.parseResult.value, + 'constraints@0', + ) as TypedConstraintDefinition; + + return new AllowlistConstraintExecutor().isValid( + value, + // Execution context with initial stack containing usage block of constraint and constraint itself + getTestExecutionContext(locator, document, services, [ + usageBlock, + constraint, + ]), + ); + } + + beforeAll(() => { + // Create language services + services = createJayveeServices(NodeFileSystem).Jayvee; + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + it('should diagnose no error on valid value', async () => { + const text = readJvTestAsset( + 'allowlist-constraint-executor/valid-constraint.jv', + ); + + const valid = await parseAndValidateConstraint(text, 'ms'); + + expect(valid).toBe(true); + }); + + it('should diagnose error on invalid value', async () => { + const text = readJvTestAsset( + 'allowlist-constraint-executor/valid-constraint.jv', + ); + + const valid = await parseAndValidateConstraint(text, 'ly'); + + expect(valid).toBe(false); + }); +}); diff --git a/libs/execution/src/lib/constraints/executors/allowlist-constraint-executor.ts b/libs/execution/src/lib/constraints/executors/allowlist-constraint-executor.ts new file mode 100644 index 00000000..5bffbff3 --- /dev/null +++ b/libs/execution/src/lib/constraints/executors/allowlist-constraint-executor.ts @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type InternalValueRepresentation } from '@jvalue/jayvee-language-server'; + +import { type ExecutionContext } from '../../execution-context'; +import { implementsStatic } from '../../util/implements-static-decorator'; +import { type ConstraintExecutor } from '../constraint-executor'; +import { type TypedConstraintExecutorClass } from '../typed-constraint-executor-class'; + +@implementsStatic<TypedConstraintExecutorClass>() +export class AllowlistConstraintExecutor implements ConstraintExecutor { + public static readonly type = 'AllowlistConstraint'; + + isValid( + value: InternalValueRepresentation, + context: ExecutionContext, + ): boolean { + if (typeof value !== 'string') { + return false; + } + + const allowlist = context.getPropertyValue( + 'allowlist', + context.valueTypeProvider.createCollectionValueTypeOf( + context.valueTypeProvider.Primitives.Text, + ), + ); + return allowlist.includes(value); + } +} diff --git a/libs/execution/src/lib/constraints/executors/denylist-constraint-executor.spec.ts b/libs/execution/src/lib/constraints/executors/denylist-constraint-executor.spec.ts new file mode 100644 index 00000000..8a0fefb0 --- /dev/null +++ b/libs/execution/src/lib/constraints/executors/denylist-constraint-executor.spec.ts @@ -0,0 +1,96 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type BlockDefinition, + type InternalValueRepresentation, + type JayveeServices, + type TypedConstraintDefinition, + createJayveeServices, +} from '@jvalue/jayvee-language-server'; +import { + type ParseHelperOptions, + expectNoParserAndLexerErrors, + parseHelper, + readJvTestAssetHelper, +} from '@jvalue/jayvee-language-server/test'; +import { + type AstNode, + type AstNodeLocator, + type LangiumDocument, +} from 'langium'; +import { NodeFileSystem } from 'langium/node'; + +import { getTestExecutionContext } from '../../../../test/utils'; + +import { DenylistConstraintExecutor } from './denylist-constraint-executor'; + +describe('Validation of DenylistConstraintExecutor', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + let locator: AstNodeLocator; + let services: JayveeServices; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../../../test/assets/', + ); + + async function parseAndValidateConstraint( + input: string, + value: InternalValueRepresentation, + ): Promise<boolean> { + const document = await parse(input, { validation: true }); + expectNoParserAndLexerErrors(document); + + const usageBlock = locator.getAstNode<BlockDefinition>( + document.parseResult.value, + 'pipelines@0/blocks@2', + ) as BlockDefinition; + const constraint = locator.getAstNode<TypedConstraintDefinition>( + document.parseResult.value, + 'constraints@0', + ) as TypedConstraintDefinition; + + return new DenylistConstraintExecutor().isValid( + value, + // Execution context with initial stack containing usage block of constraint and constraint itself + getTestExecutionContext(locator, document, services, [ + usageBlock, + constraint, + ]), + ); + } + + beforeAll(() => { + // Create language services + services = createJayveeServices(NodeFileSystem).Jayvee; + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + it('should diagnose no error on valid value', async () => { + const text = readJvTestAsset( + 'denylist-constraint-executor/valid-constraint.jv', + ); + + const valid = await parseAndValidateConstraint(text, 's'); + + expect(valid).toBe(true); + }); + + it('should diagnose error on invalid value', async () => { + const text = readJvTestAsset( + 'denylist-constraint-executor/valid-constraint.jv', + ); + + const valid = await parseAndValidateConstraint(text, 'ns'); + + expect(valid).toBe(false); + }); +}); diff --git a/libs/execution/src/lib/constraints/executors/denylist-constraint-executor.ts b/libs/execution/src/lib/constraints/executors/denylist-constraint-executor.ts new file mode 100644 index 00000000..e5494421 --- /dev/null +++ b/libs/execution/src/lib/constraints/executors/denylist-constraint-executor.ts @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type InternalValueRepresentation } from '@jvalue/jayvee-language-server'; + +import { type ExecutionContext } from '../../execution-context'; +import { implementsStatic } from '../../util/implements-static-decorator'; +import { type ConstraintExecutor } from '../constraint-executor'; +import { type TypedConstraintExecutorClass } from '../typed-constraint-executor-class'; + +@implementsStatic<TypedConstraintExecutorClass>() +export class DenylistConstraintExecutor implements ConstraintExecutor { + public static readonly type = 'DenylistConstraint'; + + isValid( + value: InternalValueRepresentation, + context: ExecutionContext, + ): boolean { + if (typeof value !== 'string') { + return false; + } + + const denylist = context.getPropertyValue( + 'denylist', + context.valueTypeProvider.createCollectionValueTypeOf( + context.valueTypeProvider.Primitives.Text, + ), + ); + return !denylist.includes(value); + } +} diff --git a/libs/execution/src/lib/constraints/executors/expression-constraint-executor.spec.ts b/libs/execution/src/lib/constraints/executors/expression-constraint-executor.spec.ts new file mode 100644 index 00000000..eb64d079 --- /dev/null +++ b/libs/execution/src/lib/constraints/executors/expression-constraint-executor.spec.ts @@ -0,0 +1,96 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type BlockDefinition, + type ExpressionConstraintDefinition, + type InternalValueRepresentation, + type JayveeServices, + createJayveeServices, +} from '@jvalue/jayvee-language-server'; +import { + type ParseHelperOptions, + expectNoParserAndLexerErrors, + parseHelper, + readJvTestAssetHelper, +} from '@jvalue/jayvee-language-server/test'; +import { + type AstNode, + type AstNodeLocator, + type LangiumDocument, +} from 'langium'; +import { NodeFileSystem } from 'langium/node'; + +import { getTestExecutionContext } from '../../../../test/utils'; + +import { ExpressionConstraintExecutor } from './expression-constraint-executor'; + +describe('Validation of AllowlistConstraintExecutor', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + let locator: AstNodeLocator; + let services: JayveeServices; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../../../test/assets/', + ); + + async function parseAndValidateConstraint( + input: string, + value: InternalValueRepresentation, + ): Promise<boolean> { + const document = await parse(input); + expectNoParserAndLexerErrors(document); + + const usageBlock = locator.getAstNode<BlockDefinition>( + document.parseResult.value, + 'pipelines@0/blocks@2', + ) as BlockDefinition; + const constraint = locator.getAstNode<ExpressionConstraintDefinition>( + document.parseResult.value, + 'constraints@0', + ) as ExpressionConstraintDefinition; + + return new ExpressionConstraintExecutor(constraint).isValid( + value, + // Execution context with initial stack containing usage block of constraint and constraint itself + getTestExecutionContext(locator, document, services, [ + usageBlock, + constraint, + ]), + ); + } + + beforeAll(() => { + // Create language services + services = createJayveeServices(NodeFileSystem).Jayvee; + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + it('should diagnose no error on valid value', async () => { + const text = readJvTestAsset( + 'expression-constraint-executor/valid-constraint.jv', + ); + + const valid = await parseAndValidateConstraint(text, 'z'); + + expect(valid).toBe(true); + }); + + it('should diagnose error on invalid value', async () => { + const text = readJvTestAsset( + 'expression-constraint-executor/valid-constraint.jv', + ); + + const valid = await parseAndValidateConstraint(text, '9'); + + expect(valid).toBe(false); + }); +}); diff --git a/libs/execution/src/lib/constraints/executors/expression-constraint-executor.ts b/libs/execution/src/lib/constraints/executors/expression-constraint-executor.ts new file mode 100644 index 00000000..ac019538 --- /dev/null +++ b/libs/execution/src/lib/constraints/executors/expression-constraint-executor.ts @@ -0,0 +1,46 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; + +import { + type AstNodeWrapper, + type ExpressionConstraintDefinition, + type InternalValueRepresentation, + evaluateExpression, +} from '@jvalue/jayvee-language-server'; + +import { type ExecutionContext } from '../../execution-context'; +import { type ConstraintExecutor } from '../constraint-executor'; + +export class ExpressionConstraintExecutor + implements ConstraintExecutor, AstNodeWrapper<ExpressionConstraintDefinition> +{ + constructor(public readonly astNode: ExpressionConstraintDefinition) {} + + isValid( + value: InternalValueRepresentation, + context: ExecutionContext, + ): boolean { + const expression = this.astNode.expression; + + context.evaluationContext.setValueForValueKeyword(value); + + const result = evaluateExpression( + expression, + context.evaluationContext, + context.wrapperFactories, + ); + assert( + context.valueTypeProvider.Primitives.Boolean.isInternalValueRepresentation( + result, + ), + ); + + context.evaluationContext.deleteValueForValueKeyword(); + + return result; + } +} diff --git a/libs/execution/src/lib/constraints/executors/length-constraint-executor.spec.ts b/libs/execution/src/lib/constraints/executors/length-constraint-executor.spec.ts new file mode 100644 index 00000000..63c7a676 --- /dev/null +++ b/libs/execution/src/lib/constraints/executors/length-constraint-executor.spec.ts @@ -0,0 +1,116 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type BlockDefinition, + type InternalValueRepresentation, + type JayveeServices, + type TypedConstraintDefinition, + createJayveeServices, +} from '@jvalue/jayvee-language-server'; +import { + type ParseHelperOptions, + expectNoParserAndLexerErrors, + parseHelper, + readJvTestAssetHelper, +} from '@jvalue/jayvee-language-server/test'; +import { + type AstNode, + type AstNodeLocator, + type LangiumDocument, +} from 'langium'; +import { NodeFileSystem } from 'langium/node'; + +import { getTestExecutionContext } from '../../../../test/utils'; + +import { LengthConstraintExecutor } from './length-constraint-executor'; + +describe('Validation of LengthConstraintExecutor', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + let locator: AstNodeLocator; + let services: JayveeServices; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../../../test/assets/', + ); + + async function parseAndValidateConstraint( + input: string, + value: InternalValueRepresentation, + ): Promise<boolean> { + const document = await parse(input, { validation: true }); + expectNoParserAndLexerErrors(document); + + const usageBlock = locator.getAstNode<BlockDefinition>( + document.parseResult.value, + 'pipelines@0/blocks@2', + ) as BlockDefinition; + const constraint = locator.getAstNode<TypedConstraintDefinition>( + document.parseResult.value, + 'constraints@0', + ) as TypedConstraintDefinition; + + return new LengthConstraintExecutor().isValid( + value, + // Execution context with initial stack containing usage block of constraint and constraint itself + getTestExecutionContext(locator, document, services, [ + usageBlock, + constraint, + ]), + ); + } + + beforeAll(() => { + // Create language services + services = createJayveeServices(NodeFileSystem).Jayvee; + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + it('should diagnose no error on valid value', async () => { + const text = readJvTestAsset( + 'length-constraint-executor/valid-constraint.jv', + ); + + const valid = await parseAndValidateConstraint(text, 'tt'); + + expect(valid).toBe(true); + }); + + it('should diagnose error on invalid value', async () => { + const text = readJvTestAsset( + 'length-constraint-executor/valid-constraint.jv', + ); + + const valid = await parseAndValidateConstraint(text, 'ttttt'); + + expect(valid).toBe(false); + }); + + it('should work with only a lower bound specified', async () => { + const text = readJvTestAsset( + 'length-constraint-executor/only-lower-bound.jv', + ); + + const valid = await parseAndValidateConstraint(text, 'morethan2chars'); + + expect(valid).toBe(true); + }); + + it('should work with only an uppper bound specified', async () => { + const text = readJvTestAsset( + 'length-constraint-executor/only-upper-bound.jv', + ); + + const valid = await parseAndValidateConstraint(text, ''); + + expect(valid).toBe(true); + }); +}); diff --git a/libs/execution/src/lib/constraints/executors/length-constraint-executor.ts b/libs/execution/src/lib/constraints/executors/length-constraint-executor.ts new file mode 100644 index 00000000..6302c99d --- /dev/null +++ b/libs/execution/src/lib/constraints/executors/length-constraint-executor.ts @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type InternalValueRepresentation } from '@jvalue/jayvee-language-server'; + +import { type ExecutionContext } from '../../execution-context'; +import { implementsStatic } from '../../util/implements-static-decorator'; +import { type ConstraintExecutor } from '../constraint-executor'; +import { type TypedConstraintExecutorClass } from '../typed-constraint-executor-class'; + +@implementsStatic<TypedConstraintExecutorClass>() +export class LengthConstraintExecutor implements ConstraintExecutor { + public static readonly type = 'LengthConstraint'; + + isValid( + value: InternalValueRepresentation, + context: ExecutionContext, + ): boolean { + if (typeof value !== 'string') { + return false; + } + + const minLength = context.getPropertyValue( + 'minLength', + context.valueTypeProvider.Primitives.Integer, + ); + const maxLength = context.getPropertyValue( + 'maxLength', + context.valueTypeProvider.Primitives.Integer, + ); + + return minLength <= value.length && value.length <= maxLength; + } +} diff --git a/libs/execution/src/lib/constraints/executors/range-constraint-executor.spec.ts b/libs/execution/src/lib/constraints/executors/range-constraint-executor.spec.ts new file mode 100644 index 00000000..57361710 --- /dev/null +++ b/libs/execution/src/lib/constraints/executors/range-constraint-executor.spec.ts @@ -0,0 +1,119 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type BlockDefinition, + type InternalValueRepresentation, + type JayveeServices, + type TypedConstraintDefinition, + createJayveeServices, + initializeWorkspace, +} from '@jvalue/jayvee-language-server'; +import { + type ParseHelperOptions, + expectNoParserAndLexerErrors, + parseHelper, + readJvTestAssetHelper, +} from '@jvalue/jayvee-language-server/test'; +import { + type AstNode, + type AstNodeLocator, + type LangiumDocument, +} from 'langium'; +import { NodeFileSystem } from 'langium/node'; + +import { getTestExecutionContext } from '../../../../test/utils'; + +import { RangeConstraintExecutor } from './range-constraint-executor'; + +describe('Validation of RangeConstraintExecutor', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + let locator: AstNodeLocator; + let services: JayveeServices; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../../../test/assets/', + ); + + async function parseAndValidateConstraint( + input: string, + value: InternalValueRepresentation, + ): Promise<boolean> { + const document = await parse(input, { validation: true }); + expectNoParserAndLexerErrors(document); + + const usageBlock = locator.getAstNode<BlockDefinition>( + document.parseResult.value, + 'pipelines@0/blocks@2', + ) as BlockDefinition; + const constraint = locator.getAstNode<TypedConstraintDefinition>( + document.parseResult.value, + 'constraints@0', + ) as TypedConstraintDefinition; + + return new RangeConstraintExecutor().isValid( + value, + // Execution context with initial stack containing usage block of constraint and constraint itself + getTestExecutionContext(locator, document, services, [ + usageBlock, + constraint, + ]), + ); + } + + beforeAll(async () => { + // Create language services + services = createJayveeServices(NodeFileSystem).Jayvee; + await initializeWorkspace(services); + + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + it('should diagnose no error on valid value', async () => { + const text = readJvTestAsset( + 'range-constraint-executor/valid-constraint.jv', + ); + + const valid = await parseAndValidateConstraint(text, 2); + + expect(valid).toBe(true); + }); + + it('should diagnose error on invalid value', async () => { + const text = readJvTestAsset( + 'range-constraint-executor/valid-constraint.jv', + ); + + const valid = await parseAndValidateConstraint(text, 11); + + expect(valid).toBe(false); + }); + + it('should work with only an upper bound specified', async () => { + const text = readJvTestAsset( + 'range-constraint-executor/only-upper-bound.jv', + ); + + const valid = await parseAndValidateConstraint(text, 0); + + expect(valid).toBe(true); + }); + + it('should work with only a lower bound specified', async () => { + const text = readJvTestAsset( + 'range-constraint-executor/only-lower-bound.jv', + ); + + const valid = await parseAndValidateConstraint(text, 999999999); + + expect(valid).toBe(true); + }); +}); diff --git a/libs/execution/src/lib/constraints/executors/range-constraint-executor.ts b/libs/execution/src/lib/constraints/executors/range-constraint-executor.ts new file mode 100644 index 00000000..7b415bd4 --- /dev/null +++ b/libs/execution/src/lib/constraints/executors/range-constraint-executor.ts @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type InternalValueRepresentation } from '@jvalue/jayvee-language-server'; + +import { type ExecutionContext } from '../../execution-context'; +import { implementsStatic } from '../../util/implements-static-decorator'; +import { type ConstraintExecutor } from '../constraint-executor'; +import { type TypedConstraintExecutorClass } from '../typed-constraint-executor-class'; + +@implementsStatic<TypedConstraintExecutorClass>() +export class RangeConstraintExecutor implements ConstraintExecutor { + public static readonly type = 'RangeConstraint'; + + isValid( + value: InternalValueRepresentation, + context: ExecutionContext, + ): boolean { + let numericValue: number; + if (typeof value === 'string') { + numericValue = Number.parseFloat(value); + } else if (typeof value === 'number') { + numericValue = value; + } else { + return false; + } + + const lowerBound = context.getPropertyValue( + 'lowerBound', + context.valueTypeProvider.Primitives.Decimal, + ); + const lowerBoundInclusive = context.getPropertyValue( + 'lowerBoundInclusive', + context.valueTypeProvider.Primitives.Boolean, + ); + const upperBound = context.getPropertyValue( + 'upperBound', + context.valueTypeProvider.Primitives.Decimal, + ); + const upperBoundInclusive = context.getPropertyValue( + 'upperBoundInclusive', + context.valueTypeProvider.Primitives.Boolean, + ); + + const lowerBoundFulfilled = lowerBoundInclusive + ? lowerBound <= numericValue + : lowerBound < numericValue; + + const upperBoundFulfilled = upperBoundInclusive + ? numericValue <= upperBound + : numericValue < upperBound; + + return lowerBoundFulfilled && upperBoundFulfilled; + } +} diff --git a/libs/execution/src/lib/constraints/executors/regex-constraint-executor.spec.ts b/libs/execution/src/lib/constraints/executors/regex-constraint-executor.spec.ts new file mode 100644 index 00000000..e1b9c764 --- /dev/null +++ b/libs/execution/src/lib/constraints/executors/regex-constraint-executor.spec.ts @@ -0,0 +1,96 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type BlockDefinition, + type InternalValueRepresentation, + type JayveeServices, + type TypedConstraintDefinition, + createJayveeServices, +} from '@jvalue/jayvee-language-server'; +import { + type ParseHelperOptions, + expectNoParserAndLexerErrors, + parseHelper, + readJvTestAssetHelper, +} from '@jvalue/jayvee-language-server/test'; +import { + type AstNode, + type AstNodeLocator, + type LangiumDocument, +} from 'langium'; +import { NodeFileSystem } from 'langium/node'; + +import { getTestExecutionContext } from '../../../../test/utils'; + +import { RegexConstraintExecutor } from './regex-constraint-executor'; + +describe('Validation of RegexConstraintExecutor', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + let locator: AstNodeLocator; + let services: JayveeServices; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../../../test/assets/', + ); + + async function parseAndValidateConstraint( + input: string, + value: InternalValueRepresentation, + ): Promise<boolean> { + const document = await parse(input, { validation: true }); + expectNoParserAndLexerErrors(document); + + const usageBlock = locator.getAstNode<BlockDefinition>( + document.parseResult.value, + 'pipelines@0/blocks@2', + ) as BlockDefinition; + const constraint = locator.getAstNode<TypedConstraintDefinition>( + document.parseResult.value, + 'constraints@0', + ) as TypedConstraintDefinition; + + return new RegexConstraintExecutor().isValid( + value, + // Execution context with initial stack containing usage block of constraint and constraint itself + getTestExecutionContext(locator, document, services, [ + usageBlock, + constraint, + ]), + ); + } + + beforeAll(() => { + // Create language services + services = createJayveeServices(NodeFileSystem).Jayvee; + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + it('should diagnose no error on valid value', async () => { + const text = readJvTestAsset( + 'regex-constraint-executor/valid-constraint.jv', + ); + + const valid = await parseAndValidateConstraint(text, 'a'); + + expect(valid).toBe(true); + }); + + it('should diagnose error on invalid value', async () => { + const text = readJvTestAsset( + 'regex-constraint-executor/valid-constraint.jv', + ); + + const valid = await parseAndValidateConstraint(text, '0'); + + expect(valid).toBe(false); + }); +}); diff --git a/libs/execution/src/lib/constraints/executors/regex-constraint-executor.ts b/libs/execution/src/lib/constraints/executors/regex-constraint-executor.ts new file mode 100644 index 00000000..8548a249 --- /dev/null +++ b/libs/execution/src/lib/constraints/executors/regex-constraint-executor.ts @@ -0,0 +1,30 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type InternalValueRepresentation } from '@jvalue/jayvee-language-server'; + +import { type ExecutionContext } from '../../execution-context'; +import { implementsStatic } from '../../util/implements-static-decorator'; +import { type ConstraintExecutor } from '../constraint-executor'; +import { type TypedConstraintExecutorClass } from '../typed-constraint-executor-class'; + +@implementsStatic<TypedConstraintExecutorClass>() +export class RegexConstraintExecutor implements ConstraintExecutor { + public static readonly type = 'RegexConstraint'; + + isValid( + value: InternalValueRepresentation, + context: ExecutionContext, + ): boolean { + if (typeof value !== 'string') { + return false; + } + + const regex = context.getPropertyValue( + 'regex', + context.valueTypeProvider.Primitives.Regex, + ); + return regex.test(value); + } +} diff --git a/libs/execution/src/lib/constraints/index.ts b/libs/execution/src/lib/constraints/index.ts new file mode 100644 index 00000000..35f9ce9a --- /dev/null +++ b/libs/execution/src/lib/constraints/index.ts @@ -0,0 +1,5 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +export * from './constraint-executor-extension'; diff --git a/libs/execution/src/lib/constraints/typed-constraint-executor-class.ts b/libs/execution/src/lib/constraints/typed-constraint-executor-class.ts new file mode 100644 index 00000000..bfd05b1f --- /dev/null +++ b/libs/execution/src/lib/constraints/typed-constraint-executor-class.ts @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type ConstructorClass } from '@jvalue/jayvee-language-server'; + +import { type ConstraintExecutor } from './constraint-executor'; + +export interface TypedConstraintExecutorClass< + T extends ConstraintExecutor = ConstraintExecutor, +> extends ConstructorClass<T> { + readonly type: string; +} diff --git a/libs/execution/src/lib/debugging/debug-configuration.ts b/libs/execution/src/lib/debugging/debug-configuration.ts new file mode 100644 index 00000000..b0dbb250 --- /dev/null +++ b/libs/execution/src/lib/debugging/debug-configuration.ts @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type BlockDefinition } from '@jvalue/jayvee-language-server'; + +import { type ExecutionContext } from '../execution-context'; + +export const DefaultGranularityValue = 'minimal'; +export const DebugGranularityValues = [ + 'peek', + 'exhaustive', + DefaultGranularityValue, +] as const; +export type DebugGranularity = (typeof DebugGranularityValues)[number]; +export function isDebugGranularity(obj: unknown): obj is DebugGranularity { + return obj === 'exhaustive' || obj === 'peek' || obj === 'minimal'; +} + +export const DefaultDebugTargetsValue = 'all'; +export type DebugTargets = string[] | typeof DefaultDebugTargetsValue; + +export function isBlockTargetedForDebugLogging( + block: BlockDefinition, + context: ExecutionContext, +): boolean { + return ( + context.runOptions.debugTargets === DefaultDebugTargetsValue || + context.runOptions.debugTargets.includes(block.name) + ); +} diff --git a/libs/execution/src/lib/debugging/debug-log-visitor.ts b/libs/execution/src/lib/debugging/debug-log-visitor.ts new file mode 100644 index 00000000..ee2812d6 --- /dev/null +++ b/libs/execution/src/lib/debugging/debug-log-visitor.ts @@ -0,0 +1,186 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type WrapperFactoryProvider, + internalValueToString, +} from '@jvalue/jayvee-language-server'; + +import { type Logger } from '../logging/logger'; +import { type Workbook } from '../types'; +import { type FileSystem } from '../types/io-types/filesystem'; +import { type BinaryFile } from '../types/io-types/filesystem-node-file-binary'; +import { type TextFile } from '../types/io-types/filesystem-node-file-text'; +import { type IoTypeVisitor } from '../types/io-types/io-type-implementation'; +import { type Sheet } from '../types/io-types/sheet'; +import { type PolarsTable, type TsTable } from '../types/io-types/table'; + +import { type DebugGranularity } from './debug-configuration'; + +export class DebugLogVisitor implements IoTypeVisitor<void> { + private readonly PEEK_NUMBER_OF_WORKBOOKS = 5; + private readonly PEEK_NUMBER_OF_ROWS = 10; + private readonly PEEK_NUMBER_OF_BYTES = 100; + private readonly PEEK_NUMBER_OF_LINES = 10; + + constructor( + private debugGranularity: DebugGranularity, + private logPrefix: string, + private logger: Logger, + private wrapperFactories: WrapperFactoryProvider, + ) {} + + visitPolarsTable(table: PolarsTable): void { + if (this.debugGranularity === 'minimal') { + return; + } + this.log(table.df.toString()); + } + + visitTsTable(table: TsTable): void { + if (this.debugGranularity === 'minimal') { + return; + } + + const numberOfRows = table.getNumberOfRows(); + this.log( + `Table with ${numberOfRows} rows and ${table.getNumberOfColumns()} columns.`, + ); + + const headers = table + .getColumns() + .map((column) => { + return `${column.name} (${column.getValueType().getName()})`; + }) + .join(' | '); + this.log(`[Header] ${headers}`); + + for (let i = 0; i < numberOfRows; ++i) { + if (this.debugGranularity === 'peek' && i >= this.PEEK_NUMBER_OF_ROWS) { + break; + } + + const rowData = table + .getRow(i) + .map((cell) => internalValueToString(cell, this.wrapperFactories)) + .join(' | '); + this.log(`[Row ${i}] ${rowData}`); + } + this.logPeekComment(); + } + + visitSheet(sheet: Sheet): void { + if (this.debugGranularity === 'minimal') { + return; + } + + this.log( + `Sheet with ${sheet.getNumberOfRows()} rows and ${sheet.getNumberOfColumns()} columns.`, + ); + const rowsAsString = sheet + .getData() + .filter((_, rowIndex) => { + if (this.debugGranularity === 'peek') { + return rowIndex < this.PEEK_NUMBER_OF_ROWS; + } + return true; + }) + .map((row) => `${row.map((cell) => `"${cell}"`).join(', ')}`); + rowsAsString.forEach((row, i) => { + this.log(`[Row ${i}] ${row}`); + }); + + this.logPeekComment(); + } + + visitNone(): void { + if (this.debugGranularity === 'minimal') { + return; + } + + this.log('<None>'); + } + + visitFileSystem(fileSystem: FileSystem): void { + if (this.debugGranularity === 'minimal') { + return; + } + + this.log(fileSystem.getFile('/')?.toString() ?? '<found no root file>'); + } + + visitBinaryFile(binaryFile: BinaryFile): void { + if (this.debugGranularity === 'minimal') { + return; + } + + const buffer = binaryFile.content.slice(0, this.PEEK_NUMBER_OF_BYTES); + const hexString = [...new Uint8Array(buffer)] + .map((x) => x.toString(16).padStart(2, '0').toUpperCase()) + .join(''); + this.log(`<hex> ${hexString}`); + this.logPeekComment(); + } + + visitTextFile(binaryFile: TextFile): void { + if (this.debugGranularity === 'minimal') { + return; + } + + for (let i = 0; i < binaryFile.content.length; ++i) { + if (i > this.PEEK_NUMBER_OF_LINES) { + break; + } + this.log(`[Line ${i}] ${binaryFile.content[i] ?? '<undefined>'}`); + } + this.logPeekComment(); + } + + private logPeekComment(): void { + if (this.debugGranularity === 'minimal') { + return; + } + + if (this.debugGranularity !== 'peek') { + return; + } + + this.log('... (omitted in peek mode)'); + } + visitWorkbook(workbook: Workbook): void { + if (this.debugGranularity === 'minimal') { + return; + } + const workbookSheets = workbook.getSheets(); + const keys = Array.from(workbookSheets.keys()); + + const numberOfSheets = workbookSheets.size; + if (numberOfSheets === 0) { + this.log(`Empty Workbook`); + return; + } + this.log(`Workbook with ${numberOfSheets} Sheets.`); + + this.log(`Sheets in WorkBook:`); + + for (let i = 0; i < numberOfSheets; ++i) { + if ( + this.debugGranularity === 'peek' && + i >= this.PEEK_NUMBER_OF_WORKBOOKS + ) { + break; + } + const currentWorkbookName = keys[i]; + if (currentWorkbookName === undefined) { + continue; + } + this.log(`WorkSheet: ${currentWorkbookName}`); + } + this.logPeekComment(); + } + + private log(text: string): void { + this.logger.logDebug(`[${this.logPrefix}] ${text}`); + } +} diff --git a/libs/execution/src/lib/debugging/index.ts b/libs/execution/src/lib/debugging/index.ts new file mode 100644 index 00000000..2cb2b735 --- /dev/null +++ b/libs/execution/src/lib/debugging/index.ts @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +export * from './debug-configuration'; +export * from './debug-log-visitor'; diff --git a/libs/execution/src/lib/execution-context.ts b/libs/execution/src/lib/execution-context.ts new file mode 100644 index 00000000..d383519f --- /dev/null +++ b/libs/execution/src/lib/execution-context.ts @@ -0,0 +1,166 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; + +import { + type BlockDefinition, + type ConstraintDefinition, + type EvaluationContext, + type InternalValueRepresentation, + type PipelineDefinition, + type PropertyAssignment, + type TransformDefinition, + type ValueType, + type ValueTypeProvider, + type WrapperFactoryProvider, + evaluatePropertyValue, + isBlockDefinition, + isExpressionConstraintDefinition, + isPipelineDefinition, + isPropertyBody, + isTransformDefinition, + isTypedConstraintDefinition, +} from '@jvalue/jayvee-language-server'; +import { assertUnreachable, isReference } from 'langium'; + +import { type JayveeConstraintExtension } from './constraints'; +import { + type DebugGranularity, + type DebugTargets, +} from './debugging/debug-configuration'; +import { type JayveeExecExtension } from './extension'; +import { type Logger } from './logging/logger'; + +export type StackNode = + | BlockDefinition + | ConstraintDefinition + | TransformDefinition; + +export class ExecutionContext { + private readonly stack: StackNode[] = []; + + constructor( + public readonly pipeline: PipelineDefinition, + public readonly executionExtension: JayveeExecExtension, + public readonly constraintExtension: JayveeConstraintExtension, + public readonly logger: Logger, + public readonly wrapperFactories: WrapperFactoryProvider, + public readonly valueTypeProvider: ValueTypeProvider, + public readonly runOptions: { + isDebugMode: boolean; + debugGranularity: DebugGranularity; + debugTargets: DebugTargets; + usePolars: boolean; + }, + public readonly evaluationContext: EvaluationContext, + ) { + logger.setLoggingContext(pipeline.name); + } + + public enterNode(node: StackNode) { + this.stack.push(node); + + this.updateLoggingContext(); + } + + public exitNode(node: StackNode) { + const poppedNode = this.stack.pop(); + assert(poppedNode === node); + + this.updateLoggingContext(); + } + + /** + * @returns the latest stack node. Returns the pipeline if the stack is empty. + */ + public getCurrentNode(): StackNode | PipelineDefinition { + const currentNode = this.stack[this.stack.length - 1]; + if (currentNode === undefined) { + return this.pipeline; + } + + return currentNode; + } + + private updateLoggingContext() { + this.logger.setLoggingDepth(this.stack.length); + this.logger.setLoggingContext(this.getCurrentNode().name); + } + + public getPropertyValue<I extends InternalValueRepresentation>( + propertyName: string, + valueType: ValueType<I>, + ): I { + const property = this.getProperty(propertyName); + + if (property === undefined) { + return this.getDefaultPropertyValue(propertyName, valueType); + } + + const propertyValue = evaluatePropertyValue( + property, + this.evaluationContext, + this.wrapperFactories, + valueType, + ); + assert(propertyValue !== undefined); + return propertyValue; + } + + public getProperty(propertyName: string): PropertyAssignment | undefined { + const currentNode = this.getCurrentNode(); + if ( + isPipelineDefinition(currentNode) || + isExpressionConstraintDefinition(currentNode) + ) { + return undefined; + } + + const body = currentNode.body; + if (!isPropertyBody(body)) { + return undefined; + } + + return body.properties.find((property) => property.name === propertyName); + } + + public getOrFailProperty(propertyName: string): PropertyAssignment { + const property = this.getProperty(propertyName); + assert(property !== undefined); + + return property; + } + + private getDefaultPropertyValue<I extends InternalValueRepresentation>( + propertyName: string, + valueType: ValueType<I>, + ): I { + const wrapper = this.getWrapperOfCurrentNode(); + const propertySpec = wrapper.getPropertySpecification(propertyName); + assert(propertySpec !== undefined); + + const defaultValue = propertySpec.defaultValue; + assert(defaultValue !== undefined); + assert(valueType.isInternalValueRepresentation(defaultValue)); + + return defaultValue; + } + + private getWrapperOfCurrentNode() { + const currentNode = this.getCurrentNode(); + assert(!isPipelineDefinition(currentNode)); + assert(!isExpressionConstraintDefinition(currentNode)); + assert(!isTransformDefinition(currentNode)); + + assert(isReference(currentNode.type)); + if (isTypedConstraintDefinition(currentNode)) { + return this.wrapperFactories.ConstraintType.wrap(currentNode.type); + } else if (isBlockDefinition(currentNode)) { + return this.wrapperFactories.BlockType.wrap(currentNode.type); + } + assertUnreachable(currentNode); + } +} diff --git a/libs/execution/src/lib/extension.ts b/libs/execution/src/lib/extension.ts new file mode 100644 index 00000000..fa5f8e3f --- /dev/null +++ b/libs/execution/src/lib/extension.ts @@ -0,0 +1,79 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; + +import { + type BlockDefinition, + isCompositeBlockTypeDefinition, +} from '@jvalue/jayvee-language-server'; + +import { type BlockExecutor } from './blocks'; +import { type BlockExecutorClass } from './blocks/block-executor-class'; +import { + createCompositeBlockExecutor, + getInputType, + getOutputType, +} from './blocks/composite-block-executor'; +import { type Logger } from './logging'; + +export abstract class JayveeExecExtension { + abstract getBlockExecutors(): BlockExecutorClass[]; + + getExecutorForBlockType( + blockTypeName: string, + usePolars: boolean, + logger: Logger, + ): BlockExecutorClass | undefined { + if (blockTypeName === 'TableInterpreter') { + blockTypeName = usePolars + ? 'PolarsTableInterpreter' + : 'TsTableInterpreter'; + } + if (blockTypeName === 'TableTransformer') { + blockTypeName = usePolars + ? 'PolarsTableTransformer' + : 'TsTableTransformer'; + } + logger.logDebug(`Trying to find executor for ${blockTypeName}`); + + return this.getBlockExecutors().find( + (x: BlockExecutorClass) => x.type === blockTypeName, + ); + } + + createBlockExecutor( + block: BlockDefinition, + usePolars: boolean, + logger: Logger, + ): BlockExecutor { + const blockType = block.type.ref; + assert(blockType !== undefined); + + let blockExecutor = this.getExecutorForBlockType( + blockType.name, + usePolars, + logger, + ); + + if ( + blockExecutor === undefined && + isCompositeBlockTypeDefinition(block.type.ref) + ) { + blockExecutor = createCompositeBlockExecutor( + getInputType(block.type.ref), + getOutputType(block.type.ref), + block, + ); + } + + assert( + blockExecutor !== undefined, + `No executor was registered for block type ${blockType.name}`, + ); + + return new blockExecutor(); + } +} diff --git a/libs/execution/src/lib/index.ts b/libs/execution/src/lib/index.ts new file mode 100644 index 00000000..cd267ae5 --- /dev/null +++ b/libs/execution/src/lib/index.ts @@ -0,0 +1,15 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +export * from './blocks'; +export * from './constraints'; +export * from './transforms'; +export * from './types'; +export * from './util'; +export * from './debugging'; +export * from './types/value-types/visitors'; + +export * from './execution-context'; +export * from './extension'; +export * from './logging'; diff --git a/libs/execution/src/lib/logging/cached-logger.ts b/libs/execution/src/lib/logging/cached-logger.ts new file mode 100644 index 00000000..4dcee74f --- /dev/null +++ b/libs/execution/src/lib/logging/cached-logger.ts @@ -0,0 +1,89 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import chalk from 'chalk'; +import { type LangiumDocument } from 'langium'; +import { type Range } from 'vscode-languageserver'; + +import { DefaultLogger } from './default-logger'; +import { LogCache } from './log-cache'; +import { DiagnosticSeverity, type LogEntry } from './logger'; + +export class CachedLogger extends DefaultLogger { + protected logCache: LogCache; + constructor( + enableDebugLogging: boolean, + loggingContext?: string, + protected printLogs = true, + depth = 0, + cacheSize = 200, + ) { + super(enableDebugLogging, loggingContext, depth); + this.logCache = new LogCache(cacheSize); + } + + /** + * Gets all log entries with the specified log levels. + * If no log level given, returns logs on all log levels. + */ + public getLogs(...logLevel: DiagnosticSeverity[]): readonly LogEntry[] { + const filterLevels = + logLevel.length === 0 ? Object.values(DiagnosticSeverity) : logLevel; + return this.logCache + .getLogs() + .filter((log) => filterLevels.includes(log.severity)); + } + + public clearLogs(): void { + this.logCache.clearLogs(); + } + + override logInfo(message: string): void { + const msg = `${chalk.bold(this.getContext())}${message}`; + this.logCache.insertLogMessage(msg, DiagnosticSeverity.INFO); + this.printMessageToPrintFnIfEnabled(msg, console.log); + } + + override logDebug(message: string): void { + if (this.enableDebugLogging) { + const msg = `${chalk.bold(this.getContext())}${message}`; + this.logCache.insertLogMessage(msg, DiagnosticSeverity.DEBUG); + this.printMessageToPrintFnIfEnabled(msg, console.log); + } + } + + override logErr(message: string): void { + const msg = `${chalk.bold(this.getContext())}${chalk.red(message)}`; + this.logCache.insertLogMessage(msg, DiagnosticSeverity.ERROR); + this.printMessageToPrintFnIfEnabled(msg, console.error); + } + + protected override logDiagnostic( + severity: DiagnosticSeverity, + message: string, + range: Range, + document: LangiumDocument, + ) { + const printFn = (msg: string) => { + const basePrintFn = this.inferPrintFunction(severity); + + this.logCache.insertLogMessage(msg, severity); + this.printMessageToPrintFnIfEnabled(msg, basePrintFn); + }; + const colorFn = this.inferChalkColor(severity); + + this.logDiagnosticMessage(severity, message, printFn, colorFn); + this.logDiagnosticInfo(range, document, printFn, colorFn); + printFn(''); + } + + printMessageToPrintFnIfEnabled( + message: string, + printFn: (message: string) => void, + ) { + if (this.printLogs) { + printFn(message); + } + } +} diff --git a/libs/execution/src/lib/logging/default-logger.ts b/libs/execution/src/lib/logging/default-logger.ts new file mode 100644 index 00000000..a61b63f5 --- /dev/null +++ b/libs/execution/src/lib/logging/default-logger.ts @@ -0,0 +1,210 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import chalk from 'chalk'; +import { type LangiumDocument } from 'langium'; +import { assertUnreachable } from 'langium'; +import { type Range } from 'vscode-languageserver'; +import { uinteger } from 'vscode-languageserver-types'; + +import { DiagnosticSeverity, Logger } from './logger'; + +export class DefaultLogger extends Logger { + private readonly TAB_TO_SPACES = 4; + + constructor( + protected readonly enableDebugLogging: boolean, + protected loggingContext?: string, + protected depth = 0, + ) { + super(); + } + + override logInfo(message: string): void { + console.log( + `${this.getDepthTabs()}${chalk.bold(this.getContext())}${message}`, + ); + } + + override logDebug(message: string): void { + if (this.enableDebugLogging) { + console.log( + `${this.getDepthTabs()}${chalk.bold(this.getContext())}${message}`, + ); + } + } + + override logErr(message: string): void { + console.error( + `${this.getDepthTabs()}${chalk.bold(this.getContext())}${chalk.red( + message, + )}`, + ); + } + + override setLoggingContext(loggingContext: string | undefined) { + this.loggingContext = loggingContext; + } + + override setLoggingDepth(depth: number): void { + this.depth = depth; + } + + private getDepthTabs(): string { + return '\t'.repeat(this.depth); + } + + protected getContext(): string { + return this.loggingContext !== undefined + ? chalk.grey(`[${this.loggingContext}] `) + : ''; + } + + protected override logDiagnostic( + severity: DiagnosticSeverity, + message: string, + range: Range, + document: LangiumDocument, + ) { + const printFn = this.inferPrintFunction(severity); + const colorFn = this.inferChalkColor(severity); + + this.logDiagnosticMessage(severity, message, printFn, colorFn); + this.logDiagnosticInfo(range, document, printFn, colorFn); + printFn(''); + } + + protected logDiagnosticMessage( + severityName: string, + message: string, + printFn: (message: string) => void, + colorFn: (message: string) => string, + ) { + printFn( + `${this.getDepthTabs()}${chalk.bold(colorFn(severityName))}: ${message}`, + ); + } + + protected logDiagnosticInfo( + range: Range, + document: LangiumDocument, + printFn: (message: string) => void, + colorFn: (message: string) => string, + ): void { + const startLineNumber = range.start.line + 1; + const endLineNumber = range.end.line + 1; + + const fullRange: Range = { + start: { + line: range.start.line, + character: 0, + }, + end: { + line: range.end.line, + character: uinteger.MAX_VALUE, + }, + }; + const text = document.textDocument.getText(fullRange).trimEnd(); + const lines = text.split('\n'); + + const lineNumberLength = Math.floor(Math.log10(endLineNumber)) + 1; + + printFn( + `${this.getDepthTabs()}$In ${document.uri.path}:${startLineNumber}:${ + range.start.character + 1 + }`, + ); + lines.forEach((line, i) => { + const lineNumber = startLineNumber + i; + const paddedLineNumber = String(lineNumber).padStart( + lineNumberLength, + ' ', + ); + printFn( + `${this.getDepthTabs()}${chalk.grey( + `${paddedLineNumber} |`, + )} ${line.replace(/\t/g, ' '.repeat(this.TAB_TO_SPACES))}`, + ); + + let underlineFrom = 0; + let underlineTo = line.length; + if (lineNumber === startLineNumber) { + underlineFrom = range.start.character; + } + if (lineNumber === endLineNumber) { + underlineTo = range.end.character; + } + + const underlineIndent = this.repeatCharAccordingToString( + ' ', + line.substring(0, underlineFrom), + this.TAB_TO_SPACES, + ); + const underline = this.repeatCharAccordingToString( + '^', + line.substring(underlineFrom, underlineTo), + this.TAB_TO_SPACES, + ); + + printFn( + `${this.getDepthTabs()}${chalk.grey( + `${' '.repeat(lineNumberLength)} |`, + )} ${underlineIndent}${colorFn(underline)}`, + ); + }); + } + + /** + * Repeats {@link charToRepeat} as many times as {@link accordingTo} is long. + * For each occurrence of \t in {@link accordingTo}, + * {@link charToRepeat} is repeated {@link tabRepeats} times instead of once. + */ + private repeatCharAccordingToString( + charToRepeat: string, + accordingTo: string, + tabRepeats: number, + ): string { + return Array.from(accordingTo).reduce((prev, cur) => { + const repeatedChar = + cur === '\t' ? charToRepeat.repeat(tabRepeats) : charToRepeat; + return `${prev}${repeatedChar}`; + }, ''); + } + + protected inferPrintFunction( + severity: DiagnosticSeverity, + ): (message: string) => void { + switch (severity) { + case DiagnosticSeverity.ERROR: + return console.error; + case DiagnosticSeverity.WARNING: + return console.warn; + case DiagnosticSeverity.INFO: + case DiagnosticSeverity.HINT: + case DiagnosticSeverity.DEBUG: + return console.info; + default: + assertUnreachable(severity); + } + } + + protected inferChalkColor( + severity: DiagnosticSeverity, + ): (message: string) => string { + switch (severity) { + case DiagnosticSeverity.ERROR: + return chalk.red; + case DiagnosticSeverity.WARNING: + return chalk.yellow; + case DiagnosticSeverity.INFO: + return chalk.green; + case DiagnosticSeverity.HINT: + return chalk.blue; + case DiagnosticSeverity.DEBUG: + return chalk.grey; + default: + assertUnreachable(severity); + } + } +} diff --git a/libs/execution/src/lib/logging/index.ts b/libs/execution/src/lib/logging/index.ts new file mode 100644 index 00000000..94235363 --- /dev/null +++ b/libs/execution/src/lib/logging/index.ts @@ -0,0 +1,7 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +export * from './logger'; +export * from './default-logger'; +export * from './cached-logger'; diff --git a/libs/execution/src/lib/logging/log-cache.ts b/libs/execution/src/lib/logging/log-cache.ts new file mode 100644 index 00000000..bc2b15df --- /dev/null +++ b/libs/execution/src/lib/logging/log-cache.ts @@ -0,0 +1,40 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type DiagnosticSeverity, type LogEntry } from './logger'; + +export class LogCache { + private logCacheMaxSize: number; + private logs: LogEntry[] = []; + + constructor(maxCacheSize = Number.POSITIVE_INFINITY) { + if (maxCacheSize <= 0) { + throw new Error('maxCacheSize needs to be greater than 0'); + } + this.logCacheMaxSize = maxCacheSize; + } + + public insertLogMessage(message: string, severity: DiagnosticSeverity) { + this.logs.push({ message, severity }); + this.removeOldestLogsIfSizeExceeded(); + } + + removeOldestLogsIfSizeExceeded() { + if (this.logs.length > this.logCacheMaxSize) { + this.logs = this.logs.slice(0, 1); + } + } + + public getLogMessages(): string[] { + return this.logs.map((e) => e.message); + } + + public getLogs(): readonly LogEntry[] { + return this.logs; + } + + public clearLogs(): void { + this.logs = []; + } +} diff --git a/libs/execution/src/lib/logging/logger.ts b/libs/execution/src/lib/logging/logger.ts new file mode 100644 index 00000000..7dc91f3f --- /dev/null +++ b/libs/execution/src/lib/logging/logger.ts @@ -0,0 +1,116 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; + +import { + type AstNode, + type DiagnosticInfo, + type LangiumDocument, + getDiagnosticRange, +} from 'langium'; +import { AstUtils } from 'langium'; +import * as ls from 'vscode-languageserver'; + +export enum DiagnosticSeverity { + ERROR = 'error', + WARNING = 'warning', + INFO = 'info', + HINT = 'hint', + DEBUG = 'debug', +} + +export interface LogEntry { + severity: DiagnosticSeverity; + message: string; +} + +export abstract class Logger { + abstract setLoggingContext(loggingContext: string | undefined): void; + abstract setLoggingDepth(depth: number): void; + abstract logInfo(message: string): void; + abstract logDebug(message: string): void; + abstract logErr(message: string): void; + + protected abstract logDiagnostic( + severity: DiagnosticSeverity, + message: string, + range: ls.Range, + document: LangiumDocument, + ): void; + + protected logLangiumDiagnostic<N extends AstNode>( + severity: DiagnosticSeverity, + message: string, + diagnostic: DiagnosticInfo<N>, + ) { + this.logDiagnostic( + severity, + message, + getDiagnosticRange(diagnostic), + AstUtils.getDocument(diagnostic.node), + ); + } + + logErrDiagnostic<N extends AstNode>( + message: string, + diagnostic: DiagnosticInfo<N>, + ): void { + this.logLangiumDiagnostic(DiagnosticSeverity.ERROR, message, diagnostic); + } + + logWarnDiagnostic<N extends AstNode>( + message: string, + diagnostic: DiagnosticInfo<N>, + ): void { + this.logLangiumDiagnostic(DiagnosticSeverity.WARNING, message, diagnostic); + } + + logInfoDiagnostic<N extends AstNode>( + message: string, + diagnostic: DiagnosticInfo<N>, + ): void { + this.logLangiumDiagnostic(DiagnosticSeverity.INFO, message, diagnostic); + } + + logHintDiagnostic<N extends AstNode>( + message: string, + diagnostic: DiagnosticInfo<N>, + ): void { + this.logLangiumDiagnostic(DiagnosticSeverity.HINT, message, diagnostic); + } + + logLanguageServerDiagnostic( + lsDiagnostic: ls.Diagnostic, + document: LangiumDocument, + ) { + assert( + lsDiagnostic.severity !== undefined, + 'The diagnostic severity is assumed to be present', + ); + + this.logDiagnostic( + this.toDiagnosticSeverity(lsDiagnostic.severity), + lsDiagnostic.message, + lsDiagnostic.range, + document, + ); + } + + private toDiagnosticSeverity( + severity: ls.DiagnosticSeverity, + ): DiagnosticSeverity { + switch (severity) { + case ls.DiagnosticSeverity.Error: + return DiagnosticSeverity.ERROR; + case ls.DiagnosticSeverity.Warning: + return DiagnosticSeverity.WARNING; + case ls.DiagnosticSeverity.Information: + return DiagnosticSeverity.INFO; + case ls.DiagnosticSeverity.Hint: + return DiagnosticSeverity.HINT; + } + } +} diff --git a/libs/execution/src/lib/transforms/index.ts b/libs/execution/src/lib/transforms/index.ts new file mode 100644 index 00000000..48a487d5 --- /dev/null +++ b/libs/execution/src/lib/transforms/index.ts @@ -0,0 +1,5 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +export * from './transform-executor'; diff --git a/libs/execution/src/lib/transforms/transform-executor.spec.ts b/libs/execution/src/lib/transforms/transform-executor.spec.ts new file mode 100644 index 00000000..dd1bf462 --- /dev/null +++ b/libs/execution/src/lib/transforms/transform-executor.spec.ts @@ -0,0 +1,411 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import assert from 'assert'; +import path from 'node:path'; + +import { + type InternalValueRepresentation, + type JayveeServices, + type TransformDefinition, + createJayveeServices, +} from '@jvalue/jayvee-language-server'; +import { + type ParseHelperOptions, + expectNoParserAndLexerErrors, + loadTestExtensions, + parseHelper, + readJvTestAssetHelper, +} from '@jvalue/jayvee-language-server/test'; +import { + type AstNode, + type AstNodeLocator, + type LangiumDocument, +} from 'langium'; +import { NodeFileSystem } from 'langium/node'; + +import { constructTable, getTestExecutionContext } from '../../../test/utils'; +import { type Table, type TableColumn } from '../types/io-types/table'; + +import { type PortDetails, TransformExecutor } from './transform-executor'; + +describe('Validation of TransformExecutor', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + let locator: AstNodeLocator; + let services: JayveeServices; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../../test/assets/', + ); + + function getColumnsMap( + inputColumnNames: string[], + inputTable: Table, + transformInputDetailsList: PortDetails[], + ): Map<string, TableColumn> { + const variableToColumnMap = new Map<string, TableColumn>(); + for (let i = 0; i < inputColumnNames.length; ++i) { + const inputColumnName = inputColumnNames[i]; + assert(inputColumnName !== undefined); + const inputColumn = inputTable.getColumn(inputColumnName); + assert(inputColumn !== undefined); + + const matchingInputDetails = transformInputDetailsList[i]; + assert(matchingInputDetails !== undefined); + + const variableName = matchingInputDetails.port.name; + variableToColumnMap.set(variableName, inputColumn); + } + return variableToColumnMap; + } + + async function parseAndExecuteTransform( + input: string, + inputTable: Table, + columnNames: string[], + ): Promise<{ + resultingColumn: TableColumn<InternalValueRepresentation>; + rowsToDelete: number[]; + }> { + const document = await parse(input, { validation: true }); + expectNoParserAndLexerErrors(document); + + const transform = locator.getAstNode<TransformDefinition>( + document.parseResult.value, + 'transforms@0', + ) as TransformDefinition; + + const executionContext = getTestExecutionContext( + locator, + document, + services, + ); + const executor = new TransformExecutor(transform, executionContext); + + return executor.executeTransform( + getColumnsMap(columnNames, inputTable, executor.getInputDetails()), + inputTable.getNumberOfRows(), + executionContext, + ); + } + + beforeAll(async () => { + // Create language services + services = createJayveeServices(NodeFileSystem).Jayvee; + + await loadTestExtensions(services, [ + path.resolve( + __dirname, + '../../../test/assets/transform-executor/test-extension/TestBlockTypes.jv', + ), + ]); + + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + it('should diagnose no error on valid value', async () => { + const text = readJvTestAsset( + 'transform-executor/valid-decimal-integer-transform.jv', + ); + + const inputTable = constructTable( + [ + { + columnName: 'Column1', + column: { + values: ['value 1'], + valueType: services.ValueTypeProvider.Primitives.Text, + }, + }, + { + columnName: 'Column2', + column: { + values: [20.2], + valueType: services.ValueTypeProvider.Primitives.Decimal, + }, + }, + ], + 1, + ); + const transformColumnNames: string[] = ['Column2']; + + const result = await parseAndExecuteTransform( + text, + inputTable, + transformColumnNames, + ); + + expect(result.rowsToDelete).toHaveLength(0); + expect(result.resultingColumn.valueType).toEqual( + services.ValueTypeProvider.Primitives.Integer, + ); + expect(result.resultingColumn.values).toHaveLength(1); + expect(result.resultingColumn.values).toEqual(expect.arrayContaining([21])); + }); + + it('should diagnose no error on invalid value representation', async () => { + const text = readJvTestAsset( + 'transform-executor/invalid-input-output-type-transform.jv', + ); + + const inputTable = constructTable( + [ + { + columnName: 'Column1', + column: { + values: ['value 1'], + valueType: services.ValueTypeProvider.Primitives.Text, + }, + }, + { + columnName: 'Column2', + column: { + values: [20.0], + valueType: services.ValueTypeProvider.Primitives.Decimal, + }, + }, + ], + 1, + ); + const transformColumnNames: string[] = ['Column2']; + + const result = await parseAndExecuteTransform( + text, + inputTable, + transformColumnNames, + ); + + expect(result.rowsToDelete).toHaveLength(1); + expect(result.rowsToDelete).toEqual(expect.arrayContaining([0])); + expect(result.resultingColumn.valueType).toEqual( + services.ValueTypeProvider.Primitives.Text, + ); + expect(result.resultingColumn.values).toHaveLength(0); + }); + + it('should diagnose no error on valid value', async () => { + const text = readJvTestAsset( + 'transform-executor/valid-multiple-input-transform.jv', + ); + + const inputTable = constructTable( + [ + { + columnName: 'Column1', + column: { + values: ['value 1'], + valueType: services.ValueTypeProvider.Primitives.Text, + }, + }, + { + columnName: 'Column2', + column: { + values: [20.2], + valueType: services.ValueTypeProvider.Primitives.Decimal, + }, + }, + { + columnName: 'Column3', + column: { + values: [85.978], + valueType: services.ValueTypeProvider.Primitives.Decimal, + }, + }, + ], + 1, + ); + const transformColumnNames: string[] = ['Column2', 'Column3']; + + const result = await parseAndExecuteTransform( + text, + inputTable, + transformColumnNames, + ); + + expect(result.rowsToDelete).toHaveLength(0); + expect(result.resultingColumn.valueType).toEqual( + services.ValueTypeProvider.Primitives.Integer, + ); + expect(result.resultingColumn.values).toHaveLength(1); + expect(result.resultingColumn.values).toEqual( + expect.arrayContaining([106]), + ); + }); + + it('should diagnose error on empty columns map', async () => { + const text = readJvTestAsset( + 'transform-executor/valid-decimal-integer-transform.jv', + ); + + const inputTable = constructTable( + [ + { + columnName: 'Column1', + column: { + values: ['value 1'], + valueType: services.ValueTypeProvider.Primitives.Text, + }, + }, + { + columnName: 'Column2', + column: { + values: [20.2], + valueType: services.ValueTypeProvider.Primitives.Decimal, + }, + }, + ], + 1, + ); + const transformColumnNames: string[] = []; + + try { + const result = await parseAndExecuteTransform( + text, + inputTable, + transformColumnNames, + ); + expect(result).toEqual(undefined); + } catch (e) { + expect(e).toBeInstanceOf(assert.AssertionError); + expect((e as assert.AssertionError).stack).toEqual( + expect.stringContaining('at TransformExecutor.addVariablesToContext'), + ); + expect((e as assert.AssertionError).expected).toEqual(true); + expect((e as assert.AssertionError).actual).toEqual(false); + } + }); + + it('should diagnose no error on invalid column type', async () => { + const text = readJvTestAsset( + 'transform-executor/valid-decimal-integer-transform.jv', + ); + + const inputTable = constructTable( + [ + { + columnName: 'Column1', + column: { + values: ['value 1'], + valueType: services.ValueTypeProvider.Primitives.Text, + }, + }, + { + columnName: 'Column2', + column: { + values: [20.2], + valueType: services.ValueTypeProvider.Primitives.Decimal, + }, + }, + ], + 1, + ); + const transformColumnNames: string[] = ['Column1']; + + const result = await parseAndExecuteTransform( + text, + inputTable, + transformColumnNames, + ); + + expect(result.rowsToDelete).toHaveLength(1); + expect(result.resultingColumn.valueType).toEqual( + services.ValueTypeProvider.Primitives.Integer, + ); + expect(result.resultingColumn.values).toHaveLength(0); + }); + + it('should diagnose no error on invalid row value', async () => { + const text = readJvTestAsset( + 'transform-executor/valid-decimal-integer-transform.jv', + ); + + const inputTable = constructTable( + [ + { + columnName: 'Column1', + column: { + values: ['value 1', 'value 2'], + valueType: services.ValueTypeProvider.Primitives.Text, + }, + }, + { + columnName: 'Column2', + column: { + values: ['20.2', 20.1], + valueType: services.ValueTypeProvider.Primitives.Decimal, + }, + }, + ], + 2, + ); + const transformColumnNames: string[] = ['Column2']; + + const result = await parseAndExecuteTransform( + text, + inputTable, + transformColumnNames, + ); + + expect(result.rowsToDelete).toHaveLength(1); + expect(result.resultingColumn.valueType).toEqual( + services.ValueTypeProvider.Primitives.Integer, + ); + expect(result.resultingColumn.values).toHaveLength(1); + expect(result.resultingColumn.values).toEqual(expect.arrayContaining([21])); + }); + + it('should diagnose no error on expression evaluation error', async () => { + const text = readJvTestAsset( + 'transform-executor/invalid-expression-evaluation-error.jv', + ); + + const inputTable = constructTable( + [ + { + columnName: 'Column1', + column: { + values: ['value 1'], + valueType: services.ValueTypeProvider.Primitives.Text, + }, + }, + { + columnName: 'Column2', + column: { + values: [20.2], + valueType: services.ValueTypeProvider.Primitives.Decimal, + }, + }, + { + columnName: 'Column3', + column: { + values: [85.978], + valueType: services.ValueTypeProvider.Primitives.Decimal, + }, + }, + ], + 1, + ); + const transformColumnNames: string[] = ['Column2', 'Column1']; + + const result = await parseAndExecuteTransform( + text, + inputTable, + transformColumnNames, + ); + + expect(result.rowsToDelete).toHaveLength(1); + expect(result.resultingColumn.valueType).toEqual( + services.ValueTypeProvider.Primitives.Decimal, + ); + expect(result.resultingColumn.values).toHaveLength(0); + }); +}); diff --git a/libs/execution/src/lib/transforms/transform-executor.ts b/libs/execution/src/lib/transforms/transform-executor.ts new file mode 100644 index 00000000..ff430692 --- /dev/null +++ b/libs/execution/src/lib/transforms/transform-executor.ts @@ -0,0 +1,262 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; + +import { + type EvaluationContext, + type InternalValueRepresentation, + type PolarsInternal, + type TransformDefinition, + type TransformOutputAssignment, + type TransformPortDefinition, + type ValueType, + evaluateExpression, + polarsEvaluateExpression, +} from '@jvalue/jayvee-language-server'; +import { zipWith } from 'fp-ts/lib/ReadonlyArray.js'; +import { pl } from 'nodejs-polars'; + +import { type ExecutionContext } from '../execution-context'; +import { isValidValueRepresentation } from '../types'; +import { type TableColumn, TsTableColumn } from '../types/io-types/table'; + +export interface PortDetails { + port: TransformPortDefinition; + valueType: ValueType; +} + +export abstract class TransformExecutor<I, O> { + constructor( + private readonly transform: TransformDefinition, + private readonly context: ExecutionContext, + ) {} + + getInputDetails(): PortDetails[] { + return this.getPortDetails('from'); + } + + getOutputDetails(): PortDetails { + const portDetails = this.getPortDetails('to'); + assert(portDetails.length === 1); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + return portDetails[0]!; + } + + protected getPortDetails(kind: TransformPortDefinition['kind']): { + port: TransformPortDefinition; + valueType: ValueType; + }[] { + const ports = this.transform.body.ports.filter((x) => x.kind === kind); + const portDetails = ports.map((port) => { + const valueTypeNode = port.valueType; + const valueType = + this.context.wrapperFactories.ValueType.wrap(valueTypeNode); + assert(valueType !== undefined); + return { + port: port, + valueType: valueType, + }; + }); + + return portDetails; + } + + getOutputAssignment(): TransformOutputAssignment { + const outputAssignments = this.transform.body.outputAssignments; + assert(outputAssignments.length === 1); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + return outputAssignments[0]!; + } + + executeTransform(input: I, context: ExecutionContext): O | undefined { + context.enterNode(this.transform); + + const result = this.doExecuteTransform(input, context); + context.exitNode(this.transform); + + return result; + } + + protected abstract doExecuteTransform( + input: I, + context: ExecutionContext, + ): O | undefined; +} + +export class PolarsTransformExecutor extends TransformExecutor< + string[], + PolarsInternal +> { + private static addInputColumnsToContext( + inputDetailsList: readonly PortDetails[], + columns: string[], + evaluationContext: EvaluationContext, + ) { + zipWith(inputDetailsList, columns, (portDetails, colName) => { + evaluationContext.setValueForReference( + portDetails.port.name, + pl.col(colName), + ); + }); + } + + protected override doExecuteTransform( + inputColumns: string[], + context: ExecutionContext, + ): PolarsInternal | undefined { + const inputDetails = this.getInputDetails(); + const outputDetails = this.getOutputDetails(); + + PolarsTransformExecutor.addInputColumnsToContext( + inputDetails, + inputColumns, + context.evaluationContext, + ); + + let expr: PolarsInternal | undefined = undefined; + try { + expr = polarsEvaluateExpression( + this.getOutputAssignment().expression, + context.evaluationContext, + context.wrapperFactories, + ); + } catch (e) { + if (e instanceof Error) { + context.logger.logDebug(e.message); + } else { + context.logger.logDebug(String(e)); + } + } + + if (expr === undefined) { + return undefined; + } + + const otype = outputDetails.valueType.toPolarsDataType(); + if (otype === undefined) { + return undefined; + } + return expr.cast(otype); + } +} + +export class TsTransformExecutor extends TransformExecutor< + { + columns: Map<string, TsTableColumn>; + numberOfRows: number; + }, + { + resultingColumn: TsTableColumn; + rowsToDelete: number[]; + } +> { + protected override doExecuteTransform( + input: { + columns: Map<string, TsTableColumn>; + numberOfRows: number; + }, + context: ExecutionContext, + ): { + resultingColumn: TsTableColumn; + rowsToDelete: number[]; + } { + const inputDetailsList = this.getInputDetails(); + const outputDetails = this.getOutputDetails(); + + const newColumn = new TsTableColumn( + outputDetails.port.name, + outputDetails.valueType, + ); + const rowsToDelete: number[] = []; + + for (let rowIndex = 0; rowIndex < input.numberOfRows; ++rowIndex) { + this.addVariablesToContext( + inputDetailsList, + input.columns, + rowIndex, + context, + ); + + let newValue: InternalValueRepresentation | undefined = undefined; + try { + newValue = evaluateExpression( + this.getOutputAssignment().expression, + context.evaluationContext, + context.wrapperFactories, + ); + } catch (e) { + if (e instanceof Error) { + context.logger.logDebug(e.message); + } else { + context.logger.logDebug(String(e)); + } + } + + if (newValue === undefined) { + context.logger.logDebug( + `Dropping row ${ + rowIndex + 1 + }: Could not evaluate transform expression`, + ); + rowsToDelete.push(rowIndex); + } else if ( + !isValidValueRepresentation(newValue, outputDetails.valueType, context) + ) { + assert( + typeof newValue === 'string' || + typeof newValue === 'boolean' || + typeof newValue === 'number', + ); + context.logger.logDebug( + `Invalid value in row ${ + rowIndex + 1 + }: "${newValue.toString()}" does not match the type ${outputDetails.valueType.getName()}`, + ); + rowsToDelete.push(rowIndex); + } else { + newColumn.push(newValue); + } + + this.removeVariablesFromContext(inputDetailsList, context); + } + + return { + rowsToDelete: rowsToDelete, + resultingColumn: newColumn, + }; + } + + private removeVariablesFromContext( + inputDetailsList: PortDetails[], + context: ExecutionContext, + ) { + for (const inputDetails of inputDetailsList) { + context.evaluationContext.deleteValueForReference(inputDetails.port.name); + } + } + + private addVariablesToContext( + inputDetailsList: PortDetails[], + columns: ReadonlyMap<string, TableColumn>, + rowIndex: number, + context: ExecutionContext, + ) { + for (const inputDetails of inputDetailsList) { + const variableName = inputDetails.port.name; + + const column = columns.get(variableName); + assert(column !== undefined); + + const variableValue = column.nth(rowIndex); + assert(variableValue != null); + + context.evaluationContext.setValueForReference( + variableName, + variableValue, + ); + } + } +} diff --git a/libs/execution/src/lib/types/index.ts b/libs/execution/src/lib/types/index.ts new file mode 100644 index 00000000..3307ba5b --- /dev/null +++ b/libs/execution/src/lib/types/index.ts @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +export * from './io-types'; +export * from './value-types'; diff --git a/libs/execution/src/lib/types/io-types/filesystem-inmemory.test.ts b/libs/execution/src/lib/types/io-types/filesystem-inmemory.test.ts new file mode 100644 index 00000000..a8339b14 --- /dev/null +++ b/libs/execution/src/lib/types/io-types/filesystem-inmemory.test.ts @@ -0,0 +1,71 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { InMemoryFileSystem } from './filesystem-inmemory'; +import { FileSystemDirectory } from './filesystem-node-directory'; +import { FileExtension, MimeType } from './filesystem-node-file'; +import { TextFile } from './filesystem-node-file-text'; + +describe('InMemoryFileSystem', () => { + let fileSystem: InMemoryFileSystem; + + beforeEach(() => { + fileSystem = new InMemoryFileSystem(); + }); + + it('returns null when the file does not exist', () => { + expect(fileSystem.getFile('/non-existent-file')).toBeNull(); + }); + + it('returns the file when it exists', () => { + const file = new TextFile( + 'existing-file', + FileExtension.TXT, + MimeType.TEXT_PLAIN, + ['test'], + ); + fileSystem.putFile('/existing-file', file); + expect(fileSystem.getFile('/existing-file')).toEqual(file); + }); + + it('returns null when the path is invalid', () => { + const file = new TextFile( + 'existing-file', + FileExtension.TXT, + MimeType.TEXT_PLAIN, + ['test'], + ); + expect(fileSystem.putFile('/invalid/path', file)).toBeNull(); + }); + + it('should return file if found using directory manually', () => { + const root = new FileSystemDirectory(''); + const dir1 = new FileSystemDirectory('dir1'); + const dir2 = new FileSystemDirectory('dir2'); + const file = new TextFile( + 'textfile', + FileExtension.TXT, + MimeType.TEXT_PLAIN, + ['test'], + ); + dir2.addChild(file); + dir1.addChild(dir2); + root.addChild(dir1); + const f = root.getNode('/dir1/dir2/textfile'.split('/')); + expect(f).toBe(file); + }); + + it('should return file if putted using directory manually', () => { + const root = new FileSystemDirectory(''); + const file = new TextFile( + 'textfile', + FileExtension.TXT, + MimeType.TEXT_PLAIN, + ['test'], + ); + + expect(root.putNode('/dir1/dir2/textfile'.split('/'), file)).toBe(file); + expect(root.getNode('/dir1/dir2/textfile'.split('/'))).toBe(file); + }); +}); diff --git a/libs/execution/src/lib/types/io-types/filesystem-inmemory.ts b/libs/execution/src/lib/types/io-types/filesystem-inmemory.ts new file mode 100644 index 00000000..c2f7bc0d --- /dev/null +++ b/libs/execution/src/lib/types/io-types/filesystem-inmemory.ts @@ -0,0 +1,88 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { IOType } from '@jvalue/jayvee-language-server'; + +import { type FileSystem } from './filesystem'; +import { FileSystemDirectory } from './filesystem-node-directory'; +import { FileSystemFile } from './filesystem-node-file'; +import { type IoTypeVisitor } from './io-type-implementation'; + +export class InMemoryFileSystem implements FileSystem { + public readonly ioType = IOType.FILE_SYSTEM; + + private rootDirectory: FileSystemDirectory = new FileSystemDirectory(''); + private static PATH_SEPARATOR = '/'; + private static CURRENT_DIR = '.'; + private static PARENT_DIR = '..'; + + /** + * Retrieves a file from the file system. Parent directory indicators (../) are resolved to up to root + * @function getFile + * @param {string} path - The absolute path to the file starting with "/..." + * @returns {FileSystemFile<unknown> | null} - The file or null if the node does not exist. + */ + getFile(path: string): FileSystemFile<unknown> | null { + const processedParts = this.processPath(path); + if (processedParts != null) { + const node = this.rootDirectory.getNode(processedParts); + if (node instanceof FileSystemFile) { + return node; + } + } + return null; + } + + /** + * Saves a file to the file system. + * @function putNode + * @param {string} path - The absolute path to the file starting with "/..." + * @param { FileSystemFile<unknown>} file - The file to save. + * @returns {FileSystem | null} - The FileSystem where file was inserted or null if the file failed to insert + */ + putFile(path: string, file: FileSystemFile<unknown>): FileSystem | null { + const processedParts = this.processPath(path); + if (processedParts != null) { + const node = this.rootDirectory.putNode(processedParts, file); + if (node instanceof FileSystemFile) { + return this; + } + } + return null; + } + + static getPathSeparator(): string { + return InMemoryFileSystem.PATH_SEPARATOR; + } + + private processPath(path: string): string[] | null { + const [head, ...tail] = path.split(InMemoryFileSystem.getPathSeparator()); + if (!(head === '' || head === InMemoryFileSystem.CURRENT_DIR)) { + return null; + } + + const parts = tail.filter((p) => p !== ''); // Process paths like "folder1//folder1" to "folder1/folder2" + const processedParts: string[] = []; + for (const part of parts) { + if (part === InMemoryFileSystem.CURRENT_DIR) { + continue; // Skip current dirs in path + } + if (part === InMemoryFileSystem.PARENT_DIR) { + const poppedPath = processedParts.pop(); // Go level up in folder hierarchy, max level up is root dir + // If Path ascend beyond root, error + if (poppedPath === undefined) { + return null; + } + } else { + processedParts.push(part); + } + } + // Add path part for root to processedParts and return + return ['', ...processedParts]; + } + + acceptVisitor<R>(visitor: IoTypeVisitor<R>): R { + return visitor.visitFileSystem(this); + } +} diff --git a/libs/execution/src/lib/types/io-types/filesystem-node-directory.ts b/libs/execution/src/lib/types/io-types/filesystem-node-directory.ts new file mode 100644 index 00000000..1d4f06af --- /dev/null +++ b/libs/execution/src/lib/types/io-types/filesystem-node-directory.ts @@ -0,0 +1,103 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only +import { FileSystemNode } from './filesystem-node'; +import { FileSystemFile } from './filesystem-node-file'; + +export class FileSystemDirectory extends FileSystemNode { + private children: FileSystemNode[] = []; + constructor(public override name: string) { + super(name); + } + + override getNode(pathParts: string[]): FileSystemNode | null { + const [firstPart, ...rest] = pathParts; + // Base case: Called a wrong node + if (firstPart !== this.name) { + return null; + } + + // Base case: Called the right node + if (rest.length === 0) { + return this; + } + + // Recursion case: Traverse child nodes + for (const child of this.children) { + const f = child.getNode(rest); + if (f) { + return f; + } + } + return null; + } + + override putNode( + pathParts: string[], + node: FileSystemNode, + ): FileSystemNode | null { + const [firstPart, ...rest] = pathParts; + if (firstPart !== this.name) { + return null; + } + // Base case: One path part left (which is the filename) + if (rest.length === 1) { + if ( + !this.nodeHasAlreadyChildFileWithSameName(rest) && + node.name === rest[0] + ) { + this.addChild(node); + return node; + } + return null; + } + + // Case: We need to add directory, because it does not exist + if ( + !this.nodeHasAlreadyChildDirectoryWithSameName(rest) && + rest[0] != null + ) { + const newdir = new FileSystemDirectory(rest[0]); + this.addChild(newdir); + return newdir.putNode(rest, node); + } + + // Recursion case: Traverse child nodes + for (const child of this.children) { + const f = child.putNode(rest, node); + if (f) { + return f; + } + } + return null; + } + + addChild(fileSystemNode: FileSystemNode): FileSystemNode | null { + this.children.push(fileSystemNode); + return fileSystemNode; + } + + private nodeHasAlreadyChildDirectoryWithSameName(rest: string[]) { + const children = this.children.filter( + (child) => child instanceof FileSystemDirectory && child.name === rest[0], + ); + return children.length !== 0; + } + private nodeHasAlreadyChildFileWithSameName(rest: string[]) { + const children = this.children.filter( + (child) => child instanceof FileSystemFile && child.name === rest[0], + ); + return children.length !== 0; + } + + override toDirectoryString(indentation = 0): string { + return ( + '\t'.repeat(indentation) + + this.name + + '\n' + + this.children + .map((child) => child.toDirectoryString(indentation + 2)) + .join('\n') + ); + } +} diff --git a/libs/execution/src/lib/types/io-types/filesystem-node-file-binary.ts b/libs/execution/src/lib/types/io-types/filesystem-node-file-binary.ts new file mode 100644 index 00000000..e25f0ad0 --- /dev/null +++ b/libs/execution/src/lib/types/io-types/filesystem-node-file-binary.ts @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { IOType } from '@jvalue/jayvee-language-server'; + +import { FileSystemFile } from './filesystem-node-file'; +import { + type IOTypeImplementation, + type IoTypeVisitor, +} from './io-type-implementation'; + +export class BinaryFile + extends FileSystemFile<ArrayBuffer> + implements IOTypeImplementation<IOType.FILE> +{ + public readonly ioType = IOType.FILE; + + acceptVisitor<R>(visitor: IoTypeVisitor<R>): R { + return visitor.visitBinaryFile(this); + } +} diff --git a/libs/execution/src/lib/types/io-types/filesystem-node-file-text.ts b/libs/execution/src/lib/types/io-types/filesystem-node-file-text.ts new file mode 100644 index 00000000..cebccb55 --- /dev/null +++ b/libs/execution/src/lib/types/io-types/filesystem-node-file-text.ts @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { IOType } from '@jvalue/jayvee-language-server'; + +import { FileSystemFile } from './filesystem-node-file'; +import { + type IOTypeImplementation, + type IoTypeVisitor, +} from './io-type-implementation'; + +export class TextFile + extends FileSystemFile<string[]> + implements IOTypeImplementation<IOType.TEXT_FILE> +{ + public readonly ioType = IOType.TEXT_FILE; + + acceptVisitor<R>(visitor: IoTypeVisitor<R>): R { + return visitor.visitTextFile(this); + } +} diff --git a/libs/execution/src/lib/types/io-types/filesystem-node-file.ts b/libs/execution/src/lib/types/io-types/filesystem-node-file.ts new file mode 100644 index 00000000..b3496d32 --- /dev/null +++ b/libs/execution/src/lib/types/io-types/filesystem-node-file.ts @@ -0,0 +1,72 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { FileSystemNode } from './filesystem-node'; + +export abstract class FileSystemFile<T> extends FileSystemNode { + constructor( + /** + * The name of the file with extension + * @property {string} name + */ + public override readonly name: string, + + /** + * The file extension in lower case, NONE / empty string for unknown or missing file extensions. + * @property {FileExtension} extension + */ + public readonly extension: FileExtension, + + /** + * The MIME type of the file taken from the Content-Type header (for HTTP requests only), + * Otherwise inferred from the file extension, default application/octet-stream for unknown or missing file extensions. + * @property {MimeType} mimeType + */ + public readonly mimeType: MimeType, + + public readonly content: T, + ) { + super(name); + } + + override getNode(pathParts: string[]): FileSystemNode | null { + const [firstPart, ...rest] = pathParts; + if (firstPart === this.name && rest.length === 0) { + return this; + } + return null; + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + override putNode(path: string[]): FileSystemNode | null { + return null; + } + + override toDirectoryString(indentation: number): string { + return ' '.repeat(indentation) + this.name + ` (${this.mimeType})`; + } +} +/** + * An enumeration of common file extensions. New extensions for Files need to be registered here. + * + * @enum {string} + */ +export enum FileExtension { + ZIP = 'zip', + TXT = 'txt', + CSV = 'csv', + NONE = '', +} + +/** + * An enumeration of common MIME types. + * + * @enum {string} + */ +export enum MimeType { + APPLICATION_ZIP = 'application/zip', + APPLICATION_OCTET_STREAM = 'application/octet-stream', + TEXT_CSV = 'text/csv', + TEXT_PLAIN = 'text/plain', +} diff --git a/libs/execution/src/lib/types/io-types/filesystem-node.ts b/libs/execution/src/lib/types/io-types/filesystem-node.ts new file mode 100644 index 00000000..3d30e264 --- /dev/null +++ b/libs/execution/src/lib/types/io-types/filesystem-node.ts @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +export abstract class FileSystemNode { + constructor(public name: string) {} + abstract getNode(pathParts: string[]): FileSystemNode | null; + abstract putNode( + pathParts: string[], + node: FileSystemNode, + ): FileSystemNode | null; + abstract toDirectoryString(indentation: number): string; +} diff --git a/libs/execution/src/lib/types/io-types/filesystem.ts b/libs/execution/src/lib/types/io-types/filesystem.ts new file mode 100644 index 00000000..7fa6b44e --- /dev/null +++ b/libs/execution/src/lib/types/io-types/filesystem.ts @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type IOType } from '@jvalue/jayvee-language-server'; + +import { type FileSystemFile } from './filesystem-node-file'; +import { type IOTypeImplementation } from './io-type-implementation'; + +/** + * FileSystem interface defines the operations that a file system implementation should have. + * @interface FileSystem + */ +export interface FileSystem extends IOTypeImplementation<IOType.FILE_SYSTEM> { + /** + * Retrieves a file from the file system. + * @function getFile + * @param {string} path - The absolute path to the file starting with "/..." + * @returns {FileSystemFile<unknown> | null} - The file or null if the node does not exist. + */ + getFile(path: string): FileSystemFile<unknown> | null; + + /** + * Saves a file to the file system. + * @function putFile + * @param {string} path - The absolute path to the file starting with "/..." + * @param { FileSystemFile<unknown>} file - The file to save. + * @returns {FileSystem | null} - The FileSystem where file was inserted or null if the file failed to insert + */ + putFile(path: string, file: FileSystemFile<unknown>): FileSystem | null; +} diff --git a/libs/execution/src/lib/types/io-types/index.ts b/libs/execution/src/lib/types/io-types/index.ts new file mode 100644 index 00000000..c4d8d7de --- /dev/null +++ b/libs/execution/src/lib/types/io-types/index.ts @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +export * from './filesystem'; +export * from './filesystem-inmemory'; +export * from './io-type-implementation'; +export * from './none'; +export * from './workbook'; +export * from './sheet'; +export * from './table'; +export * from './filesystem-node-file-binary'; +export * from './filesystem-node-file'; +export * from './filesystem-node-file-text'; +export * from './filesystem-node'; +export * from './filesystem-node-directory'; diff --git a/libs/execution/src/lib/types/io-types/io-type-implementation.ts b/libs/execution/src/lib/types/io-types/io-type-implementation.ts new file mode 100644 index 00000000..27a00207 --- /dev/null +++ b/libs/execution/src/lib/types/io-types/io-type-implementation.ts @@ -0,0 +1,30 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type IOType } from '@jvalue/jayvee-language-server'; + +import { type FileSystem } from './filesystem'; +import { type BinaryFile } from './filesystem-node-file-binary'; +import { type TextFile } from './filesystem-node-file-text'; +import { type None } from './none'; +import { type Sheet } from './sheet'; +import { type PolarsTable, type TsTable } from './table'; +import { type Workbook } from './workbook'; + +export interface IOTypeImplementation<T extends IOType = IOType> { + ioType: T; + + acceptVisitor<R = unknown>(visitor: IoTypeVisitor<R>): R; +} + +export interface IoTypeVisitor<R = unknown> { + visitTsTable(table: TsTable): R; + visitPolarsTable(table: PolarsTable): R; + visitSheet(sheet: Sheet): R; + visitWorkbook(workbook: Workbook): R; + visitNone(none: None): R; + visitFileSystem(fileSystem: FileSystem): R; + visitBinaryFile(binaryFile: BinaryFile): R; + visitTextFile(binaryFile: TextFile): R; +} diff --git a/libs/execution/src/lib/types/io-types/none.ts b/libs/execution/src/lib/types/io-types/none.ts new file mode 100644 index 00000000..8fba31f0 --- /dev/null +++ b/libs/execution/src/lib/types/io-types/none.ts @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { IOType } from '@jvalue/jayvee-language-server'; + +import { + type IOTypeImplementation, + type IoTypeVisitor, +} from './io-type-implementation'; + +export class None implements IOTypeImplementation<IOType.NONE> { + public readonly ioType = IOType.NONE; + private static _instance?: None; + + // eslint-disable-next-line @typescript-eslint/no-empty-function + private constructor() {} + + public static get singletonInstance(): None { + if (this._instance === undefined) { + this._instance = new this(); + } + return this._instance; + } + + acceptVisitor<R>(visitor: IoTypeVisitor<R>): R { + return visitor.visitNone(this); + } +} + +export const NONE = None.singletonInstance; diff --git a/libs/execution/src/lib/types/io-types/sheet.ts b/libs/execution/src/lib/types/io-types/sheet.ts new file mode 100644 index 00000000..70989b1e --- /dev/null +++ b/libs/execution/src/lib/types/io-types/sheet.ts @@ -0,0 +1,158 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; + +import { + CellIndex, + type CellIndexBounds, + type CellRangeLiteral, + type CellRangeWrapper, + type ColumnWrapper, + IOType, + type RowWrapper, + getColumnIndex, + getRowIndex, +} from '@jvalue/jayvee-language-server'; + +import { + type IOTypeImplementation, + type IoTypeVisitor, +} from './io-type-implementation'; + +export class Sheet implements IOTypeImplementation<IOType.SHEET> { + public readonly ioType = IOType.SHEET; + private numberOfRows: number; + private numberOfColumns: number; + constructor(private data: string[][]) { + this.numberOfRows = data.length; + this.numberOfColumns = data.reduce((prev, curr) => { + return curr.length > prev ? curr.length : prev; + }, 0); + } + + getData(): readonly (readonly string[])[] { + return this.data; + } + + getNumberOfRows(): number { + return this.numberOfRows; + } + + getNumberOfColumns(): number { + return this.numberOfColumns; + } + + getHeaderRow(): string[] { + assert( + this.getNumberOfRows() > 0, + 'The sheet is expected to be non-empty and have a header row', + ); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + return this.data[0]!; + } + + iterateRows(callbackFn: (row: string[], rowIndex: number) => void) { + this.data.forEach(callbackFn); + } + + clone(): Sheet { + return new Sheet(structuredClone(this.data)); + } + + deleteRow(row: RowWrapper): void { + assert(this.isInBounds(row)); + + row = this.resolveRelativeIndexes(row); + const rowIndex = getRowIndex(row); + + this.data.splice(rowIndex, 1); + this.numberOfRows--; + } + + deleteColumn(column: ColumnWrapper): void { + assert(this.isInBounds(column)); + + column = this.resolveRelativeIndexes(column); + const columnIndex = getColumnIndex(column); + + this.data.forEach((row) => { + row.splice(columnIndex, 1); + }); + this.numberOfColumns--; + } + + writeCell(absoluteCell: CellIndex, content: string): void { + assert(absoluteCell.isInBounds(this.getBounds())); + + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this.data[absoluteCell.rowIndex]![absoluteCell.columnIndex] = content; + } + + selectRange(range: CellRangeWrapper): void { + assert(this.isInBounds(range)); + + range = this.resolveRelativeIndexes(range); + + this.data = this.data.reduce<string[][]>((previous, row, rowIndex) => { + if (rowIndex < range.from.rowIndex || range.to.rowIndex < rowIndex) { + return previous; + } + return [ + ...previous, + row.slice(range.from.columnIndex, range.to.columnIndex + 1), + ]; + }, []); + + this.numberOfColumns = range.to.columnIndex - range.from.columnIndex + 1; + this.numberOfRows = range.to.rowIndex - range.from.rowIndex + 1; + } + + isInBounds(range: CellRangeWrapper): boolean { + const bounds = this.getBounds(); + return range.isInBounds(bounds); + } + + resolveRelativeIndexes<N extends CellRangeLiteral>( + range: CellRangeWrapper<N>, + ): CellRangeWrapper<N> { + if (range.hasRelativeIndexes()) { + const bounds = this.getBounds(); + return range.resolveRelativeIndexes(bounds); + } + return range; + } + + enumerateCellIndexes(range: CellRangeWrapper): CellIndex[] { + const resolvedRange = this.resolveRelativeIndexes(range); + + const result: CellIndex[] = []; + for ( + let row = resolvedRange.from.rowIndex; + row <= resolvedRange.to.rowIndex; + ++row + ) { + for ( + let column = resolvedRange.from.columnIndex; + column <= resolvedRange.to.columnIndex; + ++column + ) { + result.push(new CellIndex(column, row)); + } + } + return result; + } + + private getBounds(): CellIndexBounds { + return { + lastColumnIndex: this.numberOfColumns - 1, + lastRowIndex: this.numberOfRows - 1, + }; + } + + acceptVisitor<R>(visitor: IoTypeVisitor<R>): R { + return visitor.visitSheet(this); + } +} diff --git a/libs/execution/src/lib/types/io-types/table.spec.ts b/libs/execution/src/lib/types/io-types/table.spec.ts new file mode 100644 index 00000000..0575e26c --- /dev/null +++ b/libs/execution/src/lib/types/io-types/table.spec.ts @@ -0,0 +1,62 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { ValueTypeProvider } from '@jvalue/jayvee-language-server'; + +import { Table } from './table'; + +describe('Table', () => { + let table: Table; + let valueTypeProvider: ValueTypeProvider; + + beforeEach(() => { + table = new Table(); + valueTypeProvider = new ValueTypeProvider(); + }); + + describe('addColumn', () => { + it('should increase the number of columns correctly on adding columns', () => { + table.addColumn('a', { + valueType: valueTypeProvider.Primitives.Text, + values: [], + }); + table.addColumn('b', { + valueType: valueTypeProvider.Primitives.Text, + values: [], + }); + + expect(table.getNumberOfColumns()).toBe(2); + }); + }); + + describe('addRow', () => { + it('should increase the number of rows correctly and allow adding columns afterwards', () => { + table.addColumn('a', { + valueType: valueTypeProvider.Primitives.Text, + values: [], + }); + table.addRow({ a: 'a1' }); + table.addRow({ a: 'a2' }); + table.addRow({ a: 'a3' }); + table.addColumn('b', { + valueType: valueTypeProvider.Primitives.Text, + values: ['b1', 'b2', 'b3'], + }); + + expect(table.getNumberOfRows()).toBe(3); + }); + + it('should increase the number of rows correctly on adding a row and a given column structure', () => { + table.addColumn('a', { + valueType: valueTypeProvider.Primitives.Text, + values: [], + }); + table.addRow({ a: 'a1' }); + table.addRow({ a: 'a2' }); + table.addRow({ a: 'a3' }); + + expect(table.getNumberOfRows()).toBe(3); + }); + }); +}); diff --git a/libs/execution/src/lib/types/io-types/table.ts b/libs/execution/src/lib/types/io-types/table.ts new file mode 100644 index 00000000..4a051e52 --- /dev/null +++ b/libs/execution/src/lib/types/io-types/table.ts @@ -0,0 +1,462 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; + +import { + INTERNAL_VALUE_REPRESENTATION_TYPEGUARD, + IOType, + type InternalValueRepresentation, + type ValueType, + type ValueTypeProvider, +} from '@jvalue/jayvee-language-server'; +import { zipWith } from 'fp-ts/lib/Array.js'; +import { pl } from 'nodejs-polars'; + +import { type ExecutionContext } from '../../execution-context'; +import { + SQLColumnTypeVisitor, + SQLValueRepresentationVisitor, +} from '../value-types/visitors/'; + +import { + type IOTypeImplementation, + type IoTypeVisitor, +} from './io-type-implementation'; + +export abstract class TableColumn { + abstract getValueType(provider: ValueTypeProvider): ValueType; + abstract getName(): string; + abstract nth(n: number): InternalValueRepresentation | undefined | null; + + abstract isPolars(): this is PolarsTableColumn; + abstract isTypescript(): this is TsTableColumn; +} + +export class PolarsTableColumn extends TableColumn { + constructor(private series: pl.Series) { + super(); + } + + override getValueType(provider: ValueTypeProvider): ValueType { + return provider.fromPolarsDType(this.series.dtype); + } + + override getName(): string { + return this.series.name; + } + + override nth(n: number): InternalValueRepresentation | undefined | null { + const nth = this.series.getIndex(n) as unknown; + if (INTERNAL_VALUE_REPRESENTATION_TYPEGUARD(nth)) { + return nth; + } + if (nth == null) { + return null; + } + throw new Error( + `Expected InternalRepresentation not ${JSON.stringify( + nth, + )} (${typeof nth})`, + ); + } + + override isPolars(): this is PolarsTableColumn { + return true; + } + + override isTypescript(): this is TsTableColumn { + return false; + } + + getSeries(): Readonly<pl.Series> { + return this.series; + } +} + +export class TsTableColumn< + T extends InternalValueRepresentation = InternalValueRepresentation, +> extends TableColumn { + constructor( + public name: string, + public valueType: ValueType<T>, + public values: T[] = [], + ) { + super(); + } + + override getValueType(): ValueType<T> { + return this.valueType; + } + + override getName(): string { + return this.name; + } + + override nth(n: number): T | undefined { + return this.values.at(n); + } + + override isPolars(): this is PolarsTableColumn { + return false; + } + + override isTypescript(): this is TsTableColumn<T> { + return true; + } + + push(x: T) { + if (this.valueType.isInternalValueRepresentation(x)) { + this.values.push(x); + } + } +} + +export type TableRow = (InternalValueRepresentation | undefined)[]; +export type TableRowMap = Record< + string, + InternalValueRepresentation | undefined +>; + +export const TABLEROW_TYPEGUARD = (value: unknown): value is TableRow => + Array.isArray(value) && + value.every( + (e) => e === undefined || INTERNAL_VALUE_REPRESENTATION_TYPEGUARD(e), + ); + +/** + * Invariant: the shape of the table is always a rectangle. + * This means all columns must have the same size. + */ +export abstract class Table implements IOTypeImplementation<IOType.TABLE> { + public readonly ioType = IOType.TABLE; + + abstract withColumn(column: TableColumn): Table; + abstract getNumberOfRows(): number; + abstract getNumberOfColumns(): number; + abstract hasColumn(name: string): boolean; + abstract getColumns(): ReadonlyArray<TableColumn>; + abstract getColumn(name: string): TableColumn | undefined; + abstract getRow(id: number): TableRow; + abstract clone(): Table; + abstract acceptVisitor<R>(visitor: IoTypeVisitor<R>): R; + + abstract isPolars(): this is PolarsTable; + abstract isTypescript(): this is TsTable; + + static generateDropTableStatement(tableName: string): string { + return `DROP TABLE IF EXISTS "${tableName}";`; + } + + abstract generateInsertValuesStatement( + tableName: string, + context: ExecutionContext, + ): string; + + abstract generateCreateTableStatement( + tableName: string, + context: ExecutionContext, + ): string; +} + +export class PolarsTable extends Table { + public constructor(public df: pl.DataFrame = pl.DataFrame()) { + super(); + } + + getTypes(vts: ValueTypeProvider): ValueType[] { + return this.df.dtypes.map((dt) => vts.fromPolarsDType(dt)); + } + + override generateInsertValuesStatement( + tableName: string, + context: ExecutionContext, + ): string { + const valueRepresentationVisitor = new SQLValueRepresentationVisitor(); + + const formattedValues = this.df + .rows() + .map((row) => { + const rowValues = zipWith( + row, + this.getTypes(context.valueTypeProvider), + (e, t) => { + if (INTERNAL_VALUE_REPRESENTATION_TYPEGUARD(e)) { + return t.acceptVisitor(valueRepresentationVisitor)(e); + } + return 'NULL'; + }, + ); + return `(${rowValues.join(',')})`; + }) + .join(', '); + + const formattedColumns = this.df.columns.map((c) => `"${c}"`).join(','); + const stmnt = `INSERT INTO "${tableName}" (${formattedColumns}) VALUES ${formattedValues}`; + context.logger.logInfo(stmnt); + return stmnt; + } + + override generateCreateTableStatement( + tableName: string, + context: ExecutionContext, + ): string { + const columnTypeVisitor = new SQLColumnTypeVisitor(); + + const columnStatements = this.getColumns().map((column) => { + return `"${column.getName()}" ${column + .getValueType(context.valueTypeProvider) + .acceptVisitor(columnTypeVisitor)}`; + }); + + return `CREATE TABLE IF NOT EXISTS "${tableName}" (${columnStatements.join( + ',', + )});`; + } + + override withColumn(column: PolarsTableColumn): PolarsTable { + const ndf = this.df.withColumn(column.getSeries()); + + return new PolarsTable(ndf); + } + + withColumnByExpr(expr: pl.Expr): PolarsTable { + const ndf = this.df.withColumn(expr); + return new PolarsTable(ndf); + } + + override getNumberOfRows(): number { + return this.df.height; + } + + override getNumberOfColumns(): number { + return this.df.width; + } + + override hasColumn(name: string): boolean { + try { + this.df.getColumn(name); + return true; + } catch { + return false; + } + } + + override getColumns(): readonly PolarsTableColumn[] { + const seriess = this.df.getColumns(); + return seriess.map((s) => { + return new PolarsTableColumn(s); + }); + } + + override getColumn(name: string): PolarsTableColumn | undefined { + try { + const s = this.df.getColumn(name); + return new PolarsTableColumn(s); + } catch { + return undefined; + } + } + + override getRow(id: number): TableRow { + const row = this.df.row(id); + if (TABLEROW_TYPEGUARD(row)) { + return row; + } + throw new Error(`row: Expected InternalRepresentation not ${typeof row} `); + } + + override clone(): PolarsTable { + return new PolarsTable(this.df.clone()); + } + + override acceptVisitor<R>(visitor: IoTypeVisitor<R>): R { + return visitor.visitPolarsTable(this); + } + + override isPolars(): this is PolarsTable { + return true; + } + + override isTypescript(): this is TsTable { + return false; + } +} + +export class TsTable extends Table { + public constructor( + private numberOfRows = 0, + private columns = new Map<string, TsTableColumn>(), + ) { + super(); + } + + override withColumn(column: TsTableColumn): TsTable { + assert(column.values.length === this.numberOfRows); + const nt = this.clone(); + nt.columns.set(column.name, column); + return nt; + } + + /** + * Tries to add a new row to this table. + * NOTE: This method will only add the row if the table has at least one column! + * @param row data of this row for each column + */ + addRow(row: TableRowMap): void { + const rowLength = Object.keys(row).length; + assert( + rowLength === this.columns.size, + `Added row has the wrong dimension (expected: ${this.columns.size}, actual: ${rowLength})`, + ); + if (rowLength === 0) { + return; + } + assert( + Object.keys(row).every((x) => this.hasColumn(x)), + 'Added row does not fit the columns in the table', + ); + + Object.entries(row).forEach(([columnName, value]) => { + const column = this.columns.get(columnName); + assert(column !== undefined); + + assert(column.valueType.isInternalValueRepresentation(value)); + column.values.push(value); + }); + + this.numberOfRows++; + } + + addColumn(name: string, column: TsTableColumn): void { + assert(column.values.length === this.numberOfRows); + this.columns.set(name, column); + } + + dropRow(rowId: number): void { + assert(rowId < this.numberOfRows); + + this.columns.forEach((column) => { + column.values.splice(rowId, 1); + }); + + this.numberOfRows--; + } + + dropRows(rowIds: number[]): void { + rowIds + .sort((a, b) => b - a) // delete descending to avoid messing up row indices + .forEach((rowId) => { + this.dropRow(rowId); + }); + } + + override getNumberOfRows(): number { + return this.numberOfRows; + } + + override getNumberOfColumns(): number { + return this.columns.size; + } + + override hasColumn(name: string): boolean { + return this.columns.has(name); + } + + override getColumns(): readonly TsTableColumn[] { + return [...this.columns.values()]; + } + + override getColumn(name: string): TsTableColumn | undefined { + return this.columns.get(name); + } + + getRow(rowId: number): InternalValueRepresentation[] { + const numberOfRows = this.getNumberOfRows(); + if (rowId >= numberOfRows) { + throw new Error( + `Trying to access table row ${rowId} (of ${numberOfRows} rows)`, + ); + } + + return [...this.columns.values()].map((col) => { + const cell = col.nth(rowId); + if (cell === undefined) { + throw new Error(`Unexpected undefined for cell in row ${rowId}`); + } + return cell; + }); + } + + override generateInsertValuesStatement(tableName: string): string { + const valueRepresentationVisitor = new SQLValueRepresentationVisitor(); + + const columnNames = [ + ...this.getColumns().map((c) => { + return c.getName(); + }), + ]; + const formattedRowValues: string[] = []; + for (let rowIndex = 0; rowIndex < this.getNumberOfRows(); ++rowIndex) { + const rowValues: string[] = []; + for (const columnName of columnNames) { + const column = this.getColumn(columnName); + const entry = column?.nth(rowIndex); + assert(entry !== undefined); + let formattedValue = 'NULL'; + const tmp = column + ?.getValueType() + .acceptVisitor(valueRepresentationVisitor)(entry); + if (tmp !== undefined) { + formattedValue = tmp; + } + + rowValues.push(formattedValue); + } + formattedRowValues.push(`(${rowValues.join(',')})`); + } + + const formattedColumns = columnNames.map((c) => `"${c}"`).join(','); + + return `INSERT INTO "${tableName}" (${formattedColumns}) VALUES ${formattedRowValues.join( + ', ', + )}`; + } + + override generateCreateTableStatement(tableName: string): string { + const columnTypeVisitor = new SQLColumnTypeVisitor(); + + const columns = [...this.getColumns()]; + const columnStatements = columns.map((column) => { + return `"${column.getName()}" ${column + .getValueType() + .acceptVisitor(columnTypeVisitor)}`; + }); + + return `CREATE TABLE IF NOT EXISTS "${tableName}" (${columnStatements.join( + ',', + )});`; + } + + override clone(): TsTable { + const cloned = new TsTable( + this.numberOfRows, + structuredClone(this.columns), + ); + return cloned; + } + + override acceptVisitor<R>(visitor: IoTypeVisitor<R>): R { + return visitor.visitTsTable(this); + } + + override isPolars(): this is PolarsTable { + return false; + } + + override isTypescript(): this is TsTable { + return true; + } +} diff --git a/libs/execution/src/lib/types/io-types/workbook.ts b/libs/execution/src/lib/types/io-types/workbook.ts new file mode 100644 index 00000000..a6aab979 --- /dev/null +++ b/libs/execution/src/lib/types/io-types/workbook.ts @@ -0,0 +1,50 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { IOType } from '@jvalue/jayvee-language-server'; + +import { + type IOTypeImplementation, + type IoTypeVisitor, +} from './io-type-implementation'; +import { Sheet } from './sheet'; + +export class Workbook implements IOTypeImplementation<IOType.WORKBOOK> { + public readonly ioType = IOType.WORKBOOK; + private sheets: Map<string, Sheet> = new Map<string, Sheet>(); + + getSheets(): Map<string, Sheet> { + return this.sheets; + } + + getSheetByName(sheetName: string): Sheet | undefined { + return this.sheets.get(sheetName); + } + + getUniqueDefaultNameForNewSheet(): string { + let defaultSheetNumber = this.sheets.size; + let defaultName = `Sheet${++defaultSheetNumber}`; + + while (this.getSheetByName(defaultName) !== undefined) { + defaultName = `Sheet${++defaultSheetNumber}`; + } + return defaultName; + } + + addSheet(data: string[][], sheetName?: string): Workbook { + const sheetNameOrDefault = + sheetName ?? this.getUniqueDefaultNameForNewSheet(); + if (this.sheets.get(sheetNameOrDefault) !== undefined) { + throw new Error( + `Sheet with name ${sheetNameOrDefault} already exists in Workbook.`, + ); + } + this.sheets.set(sheetNameOrDefault, new Sheet(data)); + return this; + } + + acceptVisitor<R>(visitor: IoTypeVisitor<R>): R { + return visitor.visitWorkbook(this); + } +} diff --git a/libs/execution/src/lib/types/value-types/index.ts b/libs/execution/src/lib/types/value-types/index.ts new file mode 100644 index 00000000..6b23cf2d --- /dev/null +++ b/libs/execution/src/lib/types/value-types/index.ts @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +export * from './internal-representation-parsing'; +export * from './value-representation-validity'; diff --git a/libs/execution/src/lib/types/value-types/internal-representation-parsing.ts b/libs/execution/src/lib/types/value-types/internal-representation-parsing.ts new file mode 100644 index 00000000..39c2b032 --- /dev/null +++ b/libs/execution/src/lib/types/value-types/internal-representation-parsing.ts @@ -0,0 +1,85 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; + +import { + type AtomicValueType, + type BooleanValuetype, + type DecimalValuetype, + type IntegerValuetype, + type InternalValueRepresentation, + type TextValuetype, + type ValueType, + ValueTypeVisitor, +} from '@jvalue/jayvee-language-server'; + +export function parseValueToInternalRepresentation< + I extends InternalValueRepresentation, +>(value: string, valueType: ValueType<I>): I | undefined { + const visitor = new InternalRepresentationParserVisitor(value); + const result = valueType.acceptVisitor(visitor); + if (!valueType.isInternalValueRepresentation(result)) { + return undefined; + } + return result; +} + +class InternalRepresentationParserVisitor extends ValueTypeVisitor< + InternalValueRepresentation | undefined +> { + constructor(private value: string) { + super(); + } + + visitBoolean(vt: BooleanValuetype): boolean | undefined { + return vt.fromString(this.value); + } + + visitDecimal(vt: DecimalValuetype): number | undefined { + return vt.fromString(this.value); + } + + visitInteger(vt: IntegerValuetype): number | undefined { + return vt.fromString(this.value); + } + + visitText(vt: TextValuetype): string { + return vt.fromString(this.value); + } + + visitAtomicValueType( + valueType: AtomicValueType, + ): InternalValueRepresentation | undefined { + const supertype = valueType.getSupertype(); + assert(supertype !== undefined); + + return supertype.acceptVisitor(this); + } + + visitCellRange(): undefined { + return undefined; + } + + visitCollection(): undefined { + return undefined; + } + + visitConstraint(): undefined { + return undefined; + } + + visitRegex(): undefined { + return undefined; + } + + visitTransform(): undefined { + return undefined; + } + + visitValuetypeAssignment(): undefined { + return undefined; + } +} diff --git a/libs/execution/src/lib/types/value-types/value-representation-validity.ts b/libs/execution/src/lib/types/value-types/value-representation-validity.ts new file mode 100644 index 00000000..a99b71b0 --- /dev/null +++ b/libs/execution/src/lib/types/value-types/value-representation-validity.ts @@ -0,0 +1,119 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; + +import { + type AtomicValueType, + type BooleanValuetype, + type CellRangeValuetype, + type CollectionValueType, + type ConstraintValuetype, + type DecimalValuetype, + type IntegerValuetype, + type InternalValueRepresentation, + type PrimitiveValueType, + type RegexValuetype, + type TextValuetype, + type TransformValuetype, + type ValueType, + ValueTypeVisitor, + type ValuetypeAssignmentValuetype, +} from '@jvalue/jayvee-language-server'; + +import { type ExecutionContext } from '../../execution-context'; + +export function isValidValueRepresentation( + value: InternalValueRepresentation, + valueType: ValueType, + context: ExecutionContext, +): boolean { + const visitor = new ValueRepresentationValidityVisitor(value, context); + return valueType.acceptVisitor(visitor); +} + +class ValueRepresentationValidityVisitor extends ValueTypeVisitor<boolean> { + constructor( + private value: InternalValueRepresentation, + private context: ExecutionContext, + ) { + super(); + } + + override visitAtomicValueType(valueType: AtomicValueType): boolean { + const supertype = valueType.getSupertype(); + assert(supertype !== undefined); + if (!supertype.acceptVisitor(this)) { + return false; + } + + const constraints = valueType.getConstraints( + this.context.evaluationContext, + ); + for (const constraint of constraints) { + const constraintExecutor = + this.context.constraintExtension.createConstraintExecutor(constraint); + + this.context.enterNode(constraint); + const valueFulfilledConstraint = constraintExecutor.isValid( + this.value, + this.context, + ); + this.context.exitNode(constraint); + + if (!valueFulfilledConstraint) { + return false; + } + } + + return true; + } + + override visitBoolean(valueType: BooleanValuetype): boolean { + return this.isValidForPrimitiveValuetype(valueType); + } + + override visitDecimal(valueType: DecimalValuetype): boolean { + return this.isValidForPrimitiveValuetype(valueType); + } + + override visitInteger(valueType: IntegerValuetype): boolean { + return this.isValidForPrimitiveValuetype(valueType); + } + + override visitText(valueType: TextValuetype): boolean { + return this.isValidForPrimitiveValuetype(valueType); + } + + override visitRegex(valueType: RegexValuetype): boolean { + return this.isValidForPrimitiveValuetype(valueType); + } + + override visitCellRange(valueType: CellRangeValuetype): boolean { + return this.isValidForPrimitiveValuetype(valueType); + } + + override visitConstraint(valueType: ConstraintValuetype): boolean { + return this.isValidForPrimitiveValuetype(valueType); + } + + override visitValuetypeAssignment( + valueType: ValuetypeAssignmentValuetype, + ): boolean { + return this.isValidForPrimitiveValuetype(valueType); + } + + override visitCollection(valueType: CollectionValueType): boolean { + return this.isValidForPrimitiveValuetype(valueType); + } + + override visitTransform(valueType: TransformValuetype): boolean { + return this.isValidForPrimitiveValuetype(valueType); + } + + private isValidForPrimitiveValuetype(valueType: PrimitiveValueType): boolean { + return valueType.isInternalValueRepresentation(this.value); + } +} diff --git a/libs/execution/src/lib/types/value-types/visitors/index.ts b/libs/execution/src/lib/types/value-types/visitors/index.ts new file mode 100644 index 00000000..116fc7d3 --- /dev/null +++ b/libs/execution/src/lib/types/value-types/visitors/index.ts @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +export * from './sql-column-type-visitor'; +export * from './sql-value-representation-visitor'; diff --git a/libs/execution/src/lib/types/value-types/visitors/sql-column-type-visitor.ts b/libs/execution/src/lib/types/value-types/visitors/sql-column-type-visitor.ts new file mode 100644 index 00000000..538594ba --- /dev/null +++ b/libs/execution/src/lib/types/value-types/visitors/sql-column-type-visitor.ts @@ -0,0 +1,71 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; + +import { + type AtomicValueType, + ValueTypeVisitor, +} from '@jvalue/jayvee-language-server'; + +export class SQLColumnTypeVisitor extends ValueTypeVisitor<string> { + override visitBoolean(): string { + return 'boolean'; + } + + override visitDecimal(): string { + return 'real'; + } + + override visitInteger(): string { + return 'integer'; + } + + override visitText(): string { + return 'text'; + } + + override visitAtomicValueType(valueType: AtomicValueType): string { + const supertype = valueType.getSupertype(); + assert(supertype !== undefined); + return supertype.acceptVisitor(this); + } + + override visitRegex(): string { + throw new Error( + 'No visit implementation given for regex. Cannot be the type of a column.', + ); + } + + override visitCellRange(): string { + throw new Error( + 'No visit implementation given for cell ranges. Cannot be the type of a column.', + ); + } + + override visitConstraint(): string { + throw new Error( + 'No visit implementation given for constraints. Cannot be the type of a column.', + ); + } + + override visitValuetypeAssignment(): string { + throw new Error( + 'No visit implementation given for valueType assignments. Cannot be the type of a column.', + ); + } + + override visitCollection(): string { + throw new Error( + 'No visit implementation given for collections. Cannot be the type of a column.', + ); + } + + override visitTransform(): string { + throw new Error( + 'No visit implementation given for transforms. Cannot be the type of a column.', + ); + } +} diff --git a/libs/execution/src/lib/types/value-types/visitors/sql-value-representation-visitor.ts b/libs/execution/src/lib/types/value-types/visitors/sql-value-representation-visitor.ts new file mode 100644 index 00000000..08fc1084 --- /dev/null +++ b/libs/execution/src/lib/types/value-types/visitors/sql-value-representation-visitor.ts @@ -0,0 +1,107 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; + +import { + type AtomicValueType, + type BooleanValuetype, + type DecimalValuetype, + type IntegerValuetype, + type InternalValueRepresentation, + type TextValuetype, + ValueTypeVisitor, +} from '@jvalue/jayvee-language-server'; + +export class SQLValueRepresentationVisitor extends ValueTypeVisitor< + (value: InternalValueRepresentation) => string +> { + visitBoolean( + valueType: BooleanValuetype, + ): (value: InternalValueRepresentation) => string { + return (value: InternalValueRepresentation) => { + assert(valueType.isInternalValueRepresentation(value)); + return value ? `'true'` : `'false'`; + }; + } + + visitDecimal( + valueType: DecimalValuetype, + ): (value: InternalValueRepresentation) => string { + return (value: InternalValueRepresentation) => { + assert(valueType.isInternalValueRepresentation(value)); + return value.toString(); + }; + } + + visitInteger( + valueType: IntegerValuetype, + ): (value: InternalValueRepresentation) => string { + return (value: InternalValueRepresentation) => { + assert(valueType.isInternalValueRepresentation(value)); + return value.toString(); + }; + } + + visitText( + valueType: TextValuetype, + ): (value: InternalValueRepresentation) => string { + return (value: InternalValueRepresentation) => { + assert(valueType.isInternalValueRepresentation(value)); + const escapedValue = escapeSingleQuotes(value); + return `'${escapedValue}'`; + }; + } + + override visitAtomicValueType( + valueType: AtomicValueType, + ): (value: InternalValueRepresentation) => string { + const supertype = valueType.getSupertype(); + assert(supertype !== undefined); + return supertype.acceptVisitor(this); + } + + override visitRegex(): (value: InternalValueRepresentation) => string { + throw new Error( + 'No visit implementation given for regex. Cannot be the type of a column.', + ); + } + + override visitCellRange(): (value: InternalValueRepresentation) => string { + throw new Error( + 'No visit implementation given for cell ranges. Cannot be the type of a column.', + ); + } + + override visitConstraint(): (value: InternalValueRepresentation) => string { + throw new Error( + 'No visit implementation given for constraints. Cannot be the type of a column.', + ); + } + + override visitValuetypeAssignment(): ( + value: InternalValueRepresentation, + ) => string { + throw new Error( + 'No visit implementation given for value type assignments. Cannot be the type of a column.', + ); + } + + override visitCollection(): (value: InternalValueRepresentation) => string { + throw new Error( + 'No visit implementation given for collections. Cannot be the type of a column.', + ); + } + + override visitTransform(): (value: InternalValueRepresentation) => string { + throw new Error( + 'No visit implementation given for transforms. Cannot be the type of a column.', + ); + } +} + +function escapeSingleQuotes(value: string): string { + return value.replace(/'/g, `''`); +} diff --git a/libs/execution/src/lib/util/file-util.spec.ts b/libs/execution/src/lib/util/file-util.spec.ts new file mode 100644 index 00000000..5d307415 --- /dev/null +++ b/libs/execution/src/lib/util/file-util.spec.ts @@ -0,0 +1,71 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { FileExtension, MimeType } from '../types'; + +import { + inferFileExtensionFromContentTypeString, + inferFileExtensionFromFileExtensionString, + inferMimeTypeFromFileExtensionString, +} from './file-util'; + +describe('Validation of file-util', () => { + describe('Function inferMimeTypeFromContentTypeString', () => { + it('should diagnose no error on known mimeType', () => { + const result = inferMimeTypeFromFileExtensionString('txt'); + + expect(result).toEqual(MimeType.TEXT_PLAIN); + }); + it('should diagnose no error on undefined input', () => { + const result = inferMimeTypeFromFileExtensionString(undefined); + + expect(result).toEqual(undefined); + }); + it('should diagnose no error on unknown mimeType', () => { + const result = inferMimeTypeFromFileExtensionString('unity'); + + expect(result).toEqual(undefined); + }); + }); + describe('Function inferFileExtensionFromFileExtensionString', () => { + it('should diagnose no error on valid file extension', () => { + const result = inferFileExtensionFromFileExtensionString('txt'); + + expect(result).toEqual(FileExtension.TXT); + }); + it('should diagnose no error on valid file extension starting with .', () => { + const result = inferFileExtensionFromFileExtensionString('.txt'); + + expect(result).toEqual(FileExtension.TXT); + }); + it('should diagnose no error on undefined input', () => { + const result = inferFileExtensionFromFileExtensionString(undefined); + + expect(result).toEqual(undefined); + }); + it('should diagnose no error on unknown file extension', () => { + const result = inferFileExtensionFromFileExtensionString('unity'); + + expect(result).toEqual(undefined); + }); + }); + describe('Function inferFileExtensionFromContentTypeString', () => { + it('should diagnose no error on valid content type', () => { + const result = inferFileExtensionFromContentTypeString('text/csv'); + + expect(result).toEqual(FileExtension.CSV); + }); + it('should diagnose no error on undefined input', () => { + const result = inferFileExtensionFromContentTypeString(undefined); + + expect(result).toEqual(undefined); + }); + it('should diagnose no error on unknown content type', () => { + const result = + inferFileExtensionFromContentTypeString('application/unity'); + + expect(result).toEqual(undefined); + }); + }); +}); diff --git a/libs/execution/src/lib/util/file-util.ts b/libs/execution/src/lib/util/file-util.ts new file mode 100644 index 00000000..bab374ac --- /dev/null +++ b/libs/execution/src/lib/util/file-util.ts @@ -0,0 +1,52 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import * as mime from 'mime-types'; + +import { FileExtension, MimeType } from '../types'; + +export function inferMimeTypeFromFileExtensionString( + fileExtension: string | undefined, +): MimeType | undefined { + if (fileExtension !== undefined) { + const inferredMimeType = mime.lookup(fileExtension) as MimeType; + if (Object.values(MimeType).includes(inferredMimeType)) { + return inferredMimeType; + } + } + return undefined; +} + +export function inferFileExtensionFromFileExtensionString( + fileExtension: string | undefined, +): FileExtension | undefined { + if (fileExtension !== undefined) { + const inferredFileExtension = fileExtension.replace( + '.', + '', + ) as FileExtension; + if (Object.values(FileExtension).includes(inferredFileExtension)) { + return inferredFileExtension; + } + } + return undefined; +} + +export function inferFileExtensionFromContentTypeString( + contentType: string | undefined, +): FileExtension | undefined { + if (contentType !== undefined) { + const inferredFileExtension = mime.extension(contentType); + if (inferredFileExtension !== false) { + if ( + Object.values(FileExtension).includes( + inferredFileExtension as FileExtension, + ) + ) { + return inferredFileExtension as FileExtension; + } + } + } + return undefined; +} diff --git a/libs/execution/src/lib/util/implements-static-decorator.ts b/libs/execution/src/lib/util/implements-static-decorator.ts new file mode 100644 index 00000000..68cb7eaa --- /dev/null +++ b/libs/execution/src/lib/util/implements-static-decorator.ts @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** + * Decorator for classes, so they need to implement a given interface using static members / functions. + * + * @example + * interface SampleInterface { + * a: string + * foo(): void; + * } + * @implementsStatic<SampleInterface>() + * class SampleClass { + * static a: string = 'A'; + * static foo(): void { + * return; + * } + * } + */ +export function implementsStatic<T>() { + return <U extends T>(constructor: U) => { + constructor; + }; +} diff --git a/libs/execution/src/lib/util/index.ts b/libs/execution/src/lib/util/index.ts new file mode 100644 index 00000000..9e67d5d9 --- /dev/null +++ b/libs/execution/src/lib/util/index.ts @@ -0,0 +1,7 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +export * from './implements-static-decorator'; +export * from './file-util'; +export * from './string-util'; diff --git a/libs/execution/src/lib/util/string-util.ts b/libs/execution/src/lib/util/string-util.ts new file mode 100644 index 00000000..55b7d78b --- /dev/null +++ b/libs/execution/src/lib/util/string-util.ts @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +export function splitLines(textContent: string, lineBreak: RegExp): string[] { + const lines = textContent.split(lineBreak); + + // There may be an additional empty line due to the previous splitting + if (lines[lines.length - 1] === '') { + lines.splice(lines.length - 1, 1); + } + + return lines; +} diff --git a/libs/execution/test/assets/allowlist-constraint-executor/valid-constraint.jv b/libs/execution/test/assets/allowlist-constraint-executor/valid-constraint.jv new file mode 100644 index 00000000..782b7ffb --- /dev/null +++ b/libs/execution/test/assets/allowlist-constraint-executor/valid-constraint.jv @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +constraint TestConstraint oftype AllowlistConstraint { + allowlist: ["ms", "s", "min", "h", "d", "m", "y"]; +} + +valuetype ConstraintType oftype text { + constraints: [ + TestConstraint, + ]; +} + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestLoader oftype TestTableLoader { + } + + block TestProperty oftype TestProperty { + valuetypeAssignmentProperty: "test" oftype ConstraintType; + } + + TestExtractor -> TestProperty -> TestLoader; +} diff --git a/libs/execution/test/assets/denylist-constraint-executor/valid-constraint.jv b/libs/execution/test/assets/denylist-constraint-executor/valid-constraint.jv new file mode 100644 index 00000000..81e11499 --- /dev/null +++ b/libs/execution/test/assets/denylist-constraint-executor/valid-constraint.jv @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +constraint TestConstraint oftype DenylistConstraint { + denylist: ["ns", "ly"]; +} + +valuetype ConstraintType oftype text { + constraints: [ + TestConstraint, + ]; +} + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestLoader oftype TestTableLoader { + } + + block TestProperty oftype TestProperty { + valuetypeAssignmentProperty: "test" oftype ConstraintType; + } + + TestExtractor -> TestProperty -> TestLoader; +} diff --git a/libs/execution/test/assets/expression-constraint-executor/valid-constraint.jv b/libs/execution/test/assets/expression-constraint-executor/valid-constraint.jv new file mode 100644 index 00000000..d9f98056 --- /dev/null +++ b/libs/execution/test/assets/expression-constraint-executor/valid-constraint.jv @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +constraint TestConstraint on text: + value matches /[a-z]/; + +valuetype ConstraintType oftype text { + constraints: [ + TestConstraint, + ]; +} + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestLoader oftype TestTableLoader { + } + + block TestProperty oftype TestProperty { + valuetypeAssignmentProperty: "test" oftype ConstraintType; + } + + TestExtractor -> TestProperty -> TestLoader; +} diff --git a/libs/execution/test/assets/length-constraint-executor/only-lower-bound.jv b/libs/execution/test/assets/length-constraint-executor/only-lower-bound.jv new file mode 100644 index 00000000..4dba8f04 --- /dev/null +++ b/libs/execution/test/assets/length-constraint-executor/only-lower-bound.jv @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: 2024 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +constraint TestConstraint oftype LengthConstraint { + minLength: 2; +} + +valuetype TestValueType oftype text { + constraints: [ + TestConstraint, + ]; +} + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestLoader oftype TestTableLoader { + } + + block TestProperty oftype TestProperty { + valuetypeAssignmentProperty: "test" oftype TestValueType; + } + + TestExtractor -> TestProperty -> TestLoader; +} diff --git a/libs/execution/test/assets/length-constraint-executor/only-upper-bound.jv b/libs/execution/test/assets/length-constraint-executor/only-upper-bound.jv new file mode 100644 index 00000000..6a1904a3 --- /dev/null +++ b/libs/execution/test/assets/length-constraint-executor/only-upper-bound.jv @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: 2024 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +constraint TestConstraint oftype LengthConstraint { + maxLength: 3; +} + +valuetype TestValueType oftype text { + constraints: [ + TestConstraint, + ]; +} + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestLoader oftype TestTableLoader { + } + + block TestProperty oftype TestProperty { + valuetypeAssignmentProperty: "test" oftype TestValueType; + } + + TestExtractor -> TestProperty -> TestLoader; +} diff --git a/libs/execution/test/assets/length-constraint-executor/valid-constraint.jv b/libs/execution/test/assets/length-constraint-executor/valid-constraint.jv new file mode 100644 index 00000000..a357d49e --- /dev/null +++ b/libs/execution/test/assets/length-constraint-executor/valid-constraint.jv @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +constraint TestConstraint oftype LengthConstraint { + minLength: 2; + maxLength: 3; +} + +valuetype ConstraintType oftype text { + constraints: [ + TestConstraint, + ]; +} + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestLoader oftype TestTableLoader { + } + + block TestProperty oftype TestProperty { + valuetypeAssignmentProperty: "test" oftype ConstraintType; + } + + TestExtractor -> TestProperty -> TestLoader; +} diff --git a/libs/execution/test/assets/range-constraint-executor/only-lower-bound.jv b/libs/execution/test/assets/range-constraint-executor/only-lower-bound.jv new file mode 100644 index 00000000..67422ea0 --- /dev/null +++ b/libs/execution/test/assets/range-constraint-executor/only-lower-bound.jv @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: 2024 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +constraint TestConstraint oftype RangeConstraint { + lowerBound: 1; +} + +valuetype TestValueType oftype integer { + constraints: [ + TestConstraint, + ]; +} + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestLoader oftype TestTableLoader { + } + + block TestProperty oftype TestProperty { + valuetypeAssignmentProperty: "test" oftype TestValueType; + } + + TestExtractor -> TestProperty -> TestLoader; +} diff --git a/libs/execution/test/assets/range-constraint-executor/only-upper-bound.jv b/libs/execution/test/assets/range-constraint-executor/only-upper-bound.jv new file mode 100644 index 00000000..762dce8e --- /dev/null +++ b/libs/execution/test/assets/range-constraint-executor/only-upper-bound.jv @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: 2024 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +constraint TestConstraint oftype RangeConstraint { + upperBound: 10; +} + +valuetype TestValueType oftype integer { + constraints: [ + TestConstraint, + ]; +} + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestLoader oftype TestTableLoader { + } + + block TestProperty oftype TestProperty { + valuetypeAssignmentProperty: "test" oftype TestValueType; + } + + TestExtractor -> TestProperty -> TestLoader; +} diff --git a/libs/execution/test/assets/range-constraint-executor/valid-constraint.jv b/libs/execution/test/assets/range-constraint-executor/valid-constraint.jv new file mode 100644 index 00000000..c0234907 --- /dev/null +++ b/libs/execution/test/assets/range-constraint-executor/valid-constraint.jv @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +constraint TestConstraint oftype RangeConstraint { + lowerBound: 1; + upperBound: 10; +} + +valuetype ConstraintType oftype integer { + constraints: [ + TestConstraint, + ]; +} + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestLoader oftype TestTableLoader { + } + + block TestProperty oftype TestProperty { + valuetypeAssignmentProperty: "test" oftype ConstraintType; + } + + TestExtractor -> TestProperty -> TestLoader; +} diff --git a/libs/execution/test/assets/regex-constraint-executor/valid-constraint.jv b/libs/execution/test/assets/regex-constraint-executor/valid-constraint.jv new file mode 100644 index 00000000..565d6c66 --- /dev/null +++ b/libs/execution/test/assets/regex-constraint-executor/valid-constraint.jv @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +constraint TestConstraint oftype RegexConstraint { + regex: /[a-z]/; +} + +valuetype ConstraintType oftype text { + constraints: [ + TestConstraint, + ]; +} + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestLoader oftype TestTableLoader { + } + + block TestProperty oftype TestProperty { + valuetypeAssignmentProperty: "test" oftype ConstraintType; + } + + TestExtractor -> TestProperty -> TestLoader; +} diff --git a/libs/execution/test/assets/transform-executor/invalid-expression-evaluation-error.jv b/libs/execution/test/assets/transform-executor/invalid-expression-evaluation-error.jv new file mode 100644 index 00000000..ab8c7719 --- /dev/null +++ b/libs/execution/test/assets/transform-executor/invalid-expression-evaluation-error.jv @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +transform TestTransform { + from firstInputParam oftype decimal; + from secondInputParam oftype text; + to result oftype decimal; + + result: firstInputParam + secondInputParam; +} + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestLoader oftype TestFileLoader { + } + + TestExtractor -> TestLoader; +} diff --git a/libs/execution/test/assets/transform-executor/invalid-input-output-type-transform.jv b/libs/execution/test/assets/transform-executor/invalid-input-output-type-transform.jv new file mode 100644 index 00000000..ddb7a316 --- /dev/null +++ b/libs/execution/test/assets/transform-executor/invalid-input-output-type-transform.jv @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +transform TestTransform { + from inputParam oftype decimal; + to result oftype text; + + result: ceil(inputParam); +} + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestLoader oftype TestFileLoader { + } + + TestExtractor -> TestLoader; +} diff --git a/libs/execution/test/assets/transform-executor/test-extension/TestBlockTypes.jv b/libs/execution/test/assets/transform-executor/test-extension/TestBlockTypes.jv new file mode 100644 index 00000000..bf5bb435 --- /dev/null +++ b/libs/execution/test/assets/transform-executor/test-extension/TestBlockTypes.jv @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +builtin blocktype TestFileExtractor { + input inPort oftype None; + output outPort oftype File; +} + +builtin blocktype TestFileLoader { + input inPort oftype File; + output outPort oftype None; +} + +builtin blocktype TestTableLoader { + input inPort oftype Table; + output outPort oftype None; +} diff --git a/libs/execution/test/assets/transform-executor/valid-decimal-integer-transform.jv b/libs/execution/test/assets/transform-executor/valid-decimal-integer-transform.jv new file mode 100644 index 00000000..99a2f97b --- /dev/null +++ b/libs/execution/test/assets/transform-executor/valid-decimal-integer-transform.jv @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +transform TestTransform { + from inputParam oftype decimal; + to result oftype integer; + + result: ceil(inputParam); +} + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestLoader oftype TestFileLoader { + } + + TestExtractor -> TestLoader; +} diff --git a/libs/execution/test/assets/transform-executor/valid-multiple-input-transform.jv b/libs/execution/test/assets/transform-executor/valid-multiple-input-transform.jv new file mode 100644 index 00000000..9c56ce5a --- /dev/null +++ b/libs/execution/test/assets/transform-executor/valid-multiple-input-transform.jv @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +transform TestTransform { + from firstInputParam oftype decimal; + from secondInputParam oftype decimal; + to result oftype integer; + + result: ceil(firstInputParam) + floor(secondInputParam); +} + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestLoader oftype TestFileLoader { + } + + TestExtractor -> TestLoader; +} diff --git a/libs/execution/test/block-executor-mock.ts b/libs/execution/test/block-executor-mock.ts new file mode 100644 index 00000000..2d7c1e0d --- /dev/null +++ b/libs/execution/test/block-executor-mock.ts @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** + * Interface for defining Mocks for BlockExecutors. + * This is used to mock every external interaction of the corresponding block (e.g. web requests). + */ +export interface BlockExecutorMock { + /** + * Setup mocks for this block executor. + * @param args Optional arguments for setup, further specified in implementations + */ + setup(...args: unknown[]): Promise<void> | void; + + /** + * Restore mocks for this block executor. + */ + restore(): void; +} diff --git a/libs/execution/test/index.ts b/libs/execution/test/index.ts new file mode 100644 index 00000000..6b29ad45 --- /dev/null +++ b/libs/execution/test/index.ts @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +export * from './block-executor-mock'; +export * from './utils'; diff --git a/libs/execution/test/utils/file-util.ts b/libs/execution/test/utils/file-util.ts new file mode 100644 index 00000000..577812f5 --- /dev/null +++ b/libs/execution/test/utils/file-util.ts @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { readFileSync } from 'node:fs'; +import path from 'node:path'; + +import { + BinaryFile, + FileExtension, + MimeType, + TextFile, + inferFileExtensionFromFileExtensionString, + inferMimeTypeFromFileExtensionString, + splitLines, +} from '../../src'; + +export function createBinaryFileFromLocalFile(fileName: string): BinaryFile { + const extName = path.extname(fileName); + const mimeType = + inferMimeTypeFromFileExtensionString(extName) ?? + MimeType.APPLICATION_OCTET_STREAM; + const fileExtension = + inferFileExtensionFromFileExtensionString(extName) ?? FileExtension.NONE; + const file = readFileSync(path.resolve(__dirname, fileName)); + return new BinaryFile(path.basename(fileName), fileExtension, mimeType, file); +} + +export function createTextFileFromLocalFile(fileName: string): TextFile { + const extName = path.extname(fileName); + const mimeType = + inferMimeTypeFromFileExtensionString(extName) ?? + MimeType.APPLICATION_OCTET_STREAM; + const fileExtension = + inferFileExtensionFromFileExtensionString(extName) ?? FileExtension.NONE; + const fileContent = readFileSync(path.resolve(__dirname, fileName), 'utf-8'); + + return new TextFile( + path.basename(fileName), + fileExtension, + mimeType, + splitLines(fileContent, /\r?\n/), + ); +} diff --git a/libs/execution/test/utils/index.ts b/libs/execution/test/utils/index.ts new file mode 100644 index 00000000..144030b3 --- /dev/null +++ b/libs/execution/test/utils/index.ts @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +export * from './test-infrastructure-util'; +export * from './file-util'; diff --git a/libs/execution/test/utils/test-infrastructure-util.ts b/libs/execution/test/utils/test-infrastructure-util.ts new file mode 100644 index 00000000..572d1458 --- /dev/null +++ b/libs/execution/test/utils/test-infrastructure-util.ts @@ -0,0 +1,95 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + EvaluationContext, + type JayveeServices, + type PipelineDefinition, +} from '@jvalue/jayvee-language-server'; +import { + type AstNode, + type AstNodeLocator, + type LangiumDocument, +} from 'langium'; + +import { + type BlockExecutorClass, + CachedLogger, + type DebugGranularity, + type DebugTargets, + DefaultConstraintExtension, + ExecutionContext, + JayveeExecExtension, + type StackNode, + Table, + type TableColumn, +} from '../../src'; + +export class TestExecExtension extends JayveeExecExtension { + getBlockExecutors(): BlockExecutorClass[] { + return []; + } +} + +export function processExitMockImplementation(code?: number) { + if (code === undefined || code === 0) { + return undefined as never; + } + throw new Error(`process.exit: ${code}`); +} + +export function getTestExecutionContext( + locator: AstNodeLocator, + document: LangiumDocument<AstNode>, + services: JayveeServices, + initialStack: StackNode[] = [], + runOptions: { + isDebugMode: boolean; + debugGranularity: DebugGranularity; + debugTargets: DebugTargets; + } = { + isDebugMode: false, + debugGranularity: 'minimal', + debugTargets: 'all', + }, + loggerPrintLogs = true, +): ExecutionContext { + const pipeline = locator.getAstNode<PipelineDefinition>( + document.parseResult.value, + 'pipelines@0', + ) as PipelineDefinition; + + const executionContext = new ExecutionContext( + pipeline, + new TestExecExtension(), + new DefaultConstraintExtension(), + new CachedLogger(runOptions.isDebugMode, undefined, loggerPrintLogs), + services.WrapperFactories, + services.ValueTypeProvider, + runOptions, + new EvaluationContext( + services.RuntimeParameterProvider, + services.operators.EvaluatorRegistry, + services.ValueTypeProvider, + ), + ); + + initialStack.forEach((node) => executionContext.enterNode(node)); + + return executionContext; +} + +export interface TableColumnDefinition { + columnName: string; + column: TableColumn; +} + +export function constructTable( + columns: TableColumnDefinition[], + numberOfRows: number, +): Table { + const table = new Table(numberOfRows); + columns.forEach((col) => table.addColumn(col.columnName, col.column)); + return table; +} diff --git a/libs/execution/tsconfig.json b/libs/execution/tsconfig.json new file mode 100644 index 00000000..0e8e67d5 --- /dev/null +++ b/libs/execution/tsconfig.json @@ -0,0 +1,21 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/libs/execution/tsconfig.json.license b/libs/execution/tsconfig.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/execution/tsconfig.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/execution/tsconfig.lib.json b/libs/execution/tsconfig.lib.json new file mode 100644 index 00000000..231d6a15 --- /dev/null +++ b/libs/execution/tsconfig.lib.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "declaration": true, + "types": ["node"] + }, + "include": ["src/**/*.ts"], + "exclude": ["vite.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts", "test/**/*.ts"] +} diff --git a/libs/execution/tsconfig.lib.json.license b/libs/execution/tsconfig.lib.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/execution/tsconfig.lib.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/execution/tsconfig.spec.json b/libs/execution/tsconfig.spec.json new file mode 100644 index 00000000..b201da93 --- /dev/null +++ b/libs/execution/tsconfig.spec.json @@ -0,0 +1,27 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "types": [ + "vitest/globals", + "vitest/importMeta", + "vite/client", + "node", + "vitest" + ] + }, + "include": [ + "vite.config.ts", + "vitest.config.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.test.tsx", + "src/**/*.spec.tsx", + "src/**/*.test.js", + "src/**/*.spec.js", + "src/**/*.test.jsx", + "src/**/*.spec.jsx", + "src/**/*.d.ts", + "test/**/*.ts" + ] +} diff --git a/libs/execution/tsconfig.spec.json.license b/libs/execution/tsconfig.spec.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/execution/tsconfig.spec.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/execution/vite.config.ts b/libs/execution/vite.config.ts new file mode 100644 index 00000000..66268bdc --- /dev/null +++ b/libs/execution/vite.config.ts @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// / <reference types='vitest' /> +import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + root: __dirname, + cacheDir: '../../node_modules/.vite/libs/execution', + + plugins: [nxViteTsPaths()], + + test: { + globals: true, + cacheDir: '../../node_modules/.vitest', + environment: 'jsdom', + include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], + + reporters: ['default'], + coverage: { + reportsDirectory: '../../coverage/libs/execution', + provider: 'v8', + }, + }, +}); diff --git a/libs/extensions/rdbms/exec/.babelrc b/libs/extensions/rdbms/exec/.babelrc new file mode 100644 index 00000000..fd4cbcde --- /dev/null +++ b/libs/extensions/rdbms/exec/.babelrc @@ -0,0 +1,10 @@ +{ + "presets": [ + [ + "@nx/js/babel", + { + "useBuiltIns": "usage" + } + ] + ] +} diff --git a/libs/extensions/rdbms/exec/.babelrc.license b/libs/extensions/rdbms/exec/.babelrc.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/rdbms/exec/.babelrc.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/rdbms/exec/.eslintrc.json b/libs/extensions/rdbms/exec/.eslintrc.json new file mode 100644 index 00000000..d0cefdc8 --- /dev/null +++ b/libs/extensions/rdbms/exec/.eslintrc.json @@ -0,0 +1,21 @@ +{ + "extends": ["../../../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "parserOptions": { + "project": ["libs/extensions/rdbms/exec/tsconfig.lib.json", "libs/extensions/rdbms/exec/tsconfig.spec.json"] + }, + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/libs/extensions/rdbms/exec/.eslintrc.json.license b/libs/extensions/rdbms/exec/.eslintrc.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/rdbms/exec/.eslintrc.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/rdbms/exec/README.md b/libs/extensions/rdbms/exec/README.md new file mode 100644 index 00000000..9a9bb022 --- /dev/null +++ b/libs/extensions/rdbms/exec/README.md @@ -0,0 +1,17 @@ +<!-- +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only +--> + +# extensions-rdbms-exec + +This library was generated with [Nx](https://nx.dev). + +## Building + +Run `nx build extensions-rdbms-exec` to build the library. + +## Running unit tests + +Run `nx test extensions-rdbms-exec` to execute the unit tests via [vitest](https://vitest.dev/). diff --git a/libs/extensions/rdbms/exec/package.json b/libs/extensions/rdbms/exec/package.json new file mode 100644 index 00000000..e620ee4b --- /dev/null +++ b/libs/extensions/rdbms/exec/package.json @@ -0,0 +1,3 @@ +{ + "name": "@jvalue/jayvee-extensions/rdbms/exec" +} diff --git a/libs/extensions/rdbms/exec/package.json.license b/libs/extensions/rdbms/exec/package.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/rdbms/exec/package.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/rdbms/exec/project.json b/libs/extensions/rdbms/exec/project.json new file mode 100644 index 00000000..a8e879a4 --- /dev/null +++ b/libs/extensions/rdbms/exec/project.json @@ -0,0 +1,22 @@ +{ + "name": "extensions-rdbms-exec", + "$schema": "../../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/extensions/rdbms/exec/src", + "projectType": "library", + "targets": { + "build": {}, + "lint": { + "executor": "@nx/eslint:lint", + "outputs": ["{options.outputFile}"] + }, + "test": { + "executor": "@nx/vite:test", + "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], + "options": { + "configFile": "{projectRoot}/vite.config.ts", + "passWithNoTests": false + } + } + }, + "tags": [] +} diff --git a/libs/extensions/rdbms/exec/project.json.license b/libs/extensions/rdbms/exec/project.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/rdbms/exec/project.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/rdbms/exec/src/extension.ts b/libs/extensions/rdbms/exec/src/extension.ts new file mode 100644 index 00000000..2163f9f8 --- /dev/null +++ b/libs/extensions/rdbms/exec/src/extension.ts @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type BlockExecutorClass, + JayveeExecExtension, +} from '@jvalue/jayvee-execution'; + +import { PostgresLoaderExecutor, SQLiteLoaderExecutor } from './lib'; + +export class RdbmsExecExtension extends JayveeExecExtension { + getBlockExecutors(): BlockExecutorClass[] { + return [PostgresLoaderExecutor, SQLiteLoaderExecutor]; + } +} diff --git a/libs/extensions/rdbms/exec/src/index.ts b/libs/extensions/rdbms/exec/src/index.ts new file mode 100644 index 00000000..88d3a004 --- /dev/null +++ b/libs/extensions/rdbms/exec/src/index.ts @@ -0,0 +1,5 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +export * from './extension'; diff --git a/libs/extensions/rdbms/exec/src/lib/index.ts b/libs/extensions/rdbms/exec/src/lib/index.ts new file mode 100644 index 00000000..7978e51f --- /dev/null +++ b/libs/extensions/rdbms/exec/src/lib/index.ts @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +export * from './postgres-loader-executor'; +export * from './sqlite-loader-executor'; diff --git a/libs/extensions/rdbms/exec/src/lib/postgres-loader-executor.spec.ts b/libs/extensions/rdbms/exec/src/lib/postgres-loader-executor.spec.ts new file mode 100644 index 00000000..51e9d4e1 --- /dev/null +++ b/libs/extensions/rdbms/exec/src/lib/postgres-loader-executor.spec.ts @@ -0,0 +1,184 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import path from 'node:path'; + +import * as R from '@jvalue/jayvee-execution'; +import { + constructTable, + getTestExecutionContext, +} from '@jvalue/jayvee-execution/test'; +import { + type BlockDefinition, + IOType, + type JayveeServices, + createJayveeServices, +} from '@jvalue/jayvee-language-server'; +import { + type ParseHelperOptions, + expectNoParserAndLexerErrors, + loadTestExtensions, + parseHelper, + readJvTestAssetHelper, +} from '@jvalue/jayvee-language-server/test'; +import { + type AstNode, + type AstNodeLocator, + type LangiumDocument, +} from 'langium'; +import { NodeFileSystem } from 'langium/node'; +import { type Mock, vi } from 'vitest'; + +import { PostgresLoaderExecutor } from './postgres-loader-executor'; + +// eslint-disable-next-line no-var +var databaseConnectMock: Mock; +// eslint-disable-next-line no-var +var databaseQueryMock: Mock; +// eslint-disable-next-line no-var +var databaseEndMock: Mock; +vi.mock('pg', () => { + databaseConnectMock = vi.fn(); + databaseQueryMock = vi.fn(); + databaseEndMock = vi.fn(); + const mClient = { + connect: databaseConnectMock, + query: databaseQueryMock, + end: databaseEndMock, + }; + return { + default: { + Client: vi.fn(() => mClient), + }, + }; +}); + +describe('Validation of PostgresLoaderExecutor', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + let locator: AstNodeLocator; + let services: JayveeServices; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../test/assets/postgres-loader-executor/', + ); + + async function parseAndExecuteExecutor( + input: string, + IOInput: R.Table, + ): Promise<R.Result<R.None>> { + const document = await parse(input, { validation: true }); + expectNoParserAndLexerErrors(document); + + const block = locator.getAstNode<BlockDefinition>( + document.parseResult.value, + 'pipelines@0/blocks@1', + ) as BlockDefinition; + + return new PostgresLoaderExecutor().doExecute( + IOInput, + getTestExecutionContext(locator, document, services, [block]), + ); + } + + beforeAll(async () => { + // Create language services + services = createJayveeServices(NodeFileSystem).Jayvee; + await loadTestExtensions(services, [ + path.resolve(__dirname, '../../test/test-extension/TestBlockTypes.jv'), + ]); + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + afterEach(() => { + vi.clearAllMocks(); + }); + + it('should diagnose no error on valid loader config', async () => { + const text = readJvTestAsset('valid-postgres-loader.jv'); + + const inputTable = constructTable( + [ + { + columnName: 'Column1', + column: { + values: ['value 1'], + valueType: services.ValueTypeProvider.Primitives.Text, + }, + }, + { + columnName: 'Column2', + column: { + values: [20.2], + valueType: services.ValueTypeProvider.Primitives.Decimal, + }, + }, + ], + 1, + ); + const result = await parseAndExecuteExecutor(text, inputTable); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.NONE); + expect(databaseConnectMock).toBeCalledTimes(1); + expect(databaseQueryMock).nthCalledWith( + 1, + 'DROP TABLE IF EXISTS "Test";', + ); + expect(databaseQueryMock).nthCalledWith( + 2, + `CREATE TABLE IF NOT EXISTS "Test" ("Column1" text,"Column2" real);`, + ); + expect(databaseQueryMock).nthCalledWith( + 3, + `INSERT INTO "Test" ("Column1","Column2") VALUES ('value 1',20.2)`, + ); + expect(databaseEndMock).toBeCalledTimes(1); + } + }); + + it('should diagnose error on pg client connect error', async () => { + const text = readJvTestAsset('valid-postgres-loader.jv'); + + const inputTable = constructTable( + [ + { + columnName: 'Column1', + column: { + values: ['value 1'], + valueType: services.ValueTypeProvider.Primitives.Text, + }, + }, + { + columnName: 'Column2', + column: { + values: [20.2], + valueType: services.ValueTypeProvider.Primitives.Decimal, + }, + }, + ], + 1, + ); + databaseConnectMock.mockImplementation(() => { + throw new Error('Connection error'); + }); + const result = await parseAndExecuteExecutor(text, inputTable); + + expect(R.isOk(result)).toEqual(false); + if (R.isErr(result)) { + expect(result.left.message).toEqual( + 'Could not write to postgres database: Connection error', + ); + expect(databaseConnectMock).toBeCalledTimes(1); + expect(databaseQueryMock).toBeCalledTimes(0); + expect(databaseEndMock).toBeCalledTimes(1); + } + }); +}); diff --git a/libs/extensions/rdbms/exec/src/lib/postgres-loader-executor.ts b/libs/extensions/rdbms/exec/src/lib/postgres-loader-executor.ts new file mode 100644 index 00000000..4cfed923 --- /dev/null +++ b/libs/extensions/rdbms/exec/src/lib/postgres-loader-executor.ts @@ -0,0 +1,98 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import * as R from '@jvalue/jayvee-execution'; +import { + AbstractBlockExecutor, + type BlockExecutorClass, + type ExecutionContext, + NONE, + type None, + Table, + implementsStatic, +} from '@jvalue/jayvee-execution'; +import { IOType } from '@jvalue/jayvee-language-server'; +import pg from 'pg'; + +const { Client } = pg; // work around import issue with ESM + +@implementsStatic<BlockExecutorClass>() +export class PostgresLoaderExecutor extends AbstractBlockExecutor< + IOType.TABLE, + IOType.NONE +> { + public static readonly type = 'PostgresLoader'; + + constructor() { + super(IOType.TABLE, IOType.NONE); + } + + async doExecute( + input: Table, + context: ExecutionContext, + ): Promise<R.Result<None>> { + const host = context.getPropertyValue( + 'host', + context.valueTypeProvider.Primitives.Text, + ); + const port = context.getPropertyValue( + 'port', + context.valueTypeProvider.Primitives.Integer, + ); + const user = context.getPropertyValue( + 'username', + context.valueTypeProvider.Primitives.Text, + ); + const password = context.getPropertyValue( + 'password', + context.valueTypeProvider.Primitives.Text, + ); + const database = context.getPropertyValue( + 'database', + context.valueTypeProvider.Primitives.Text, + ); + const table = context.getPropertyValue( + 'table', + context.valueTypeProvider.Primitives.Text, + ); + + const client = new Client({ + host, + port, + user, + password, + database, + }); + + try { + context.logger.logDebug(`Connecting to database`); + await client.connect(); + + context.logger.logDebug( + `Dropping previous table "${table}" if it exists`, + ); + await client.query(Table.generateDropTableStatement(table)); + context.logger.logDebug(`Creating table "${table}"`); + await client.query(input.generateCreateTableStatement(table, context)); + context.logger.logDebug( + `Inserting ${input.getNumberOfRows()} row(s) into table "${table}"`, + ); + await client.query(input.generateInsertValuesStatement(table, context)); + + context.logger.logDebug( + `The data was successfully loaded into the database`, + ); + return R.ok(NONE); + } catch (err: unknown) { + return R.err({ + message: `Could not write to postgres database: ${ + err instanceof Error ? err.message : JSON.stringify(err) + }`, + diagnostic: { node: context.getCurrentNode(), property: 'name' }, + }); + } finally { + await client.end(); + } + } +} diff --git a/libs/extensions/rdbms/exec/src/lib/sqlite-loader-executor.spec.ts b/libs/extensions/rdbms/exec/src/lib/sqlite-loader-executor.spec.ts new file mode 100644 index 00000000..7cfc883b --- /dev/null +++ b/libs/extensions/rdbms/exec/src/lib/sqlite-loader-executor.spec.ts @@ -0,0 +1,208 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import path from 'node:path'; + +import * as R from '@jvalue/jayvee-execution'; +import { + constructTable, + getTestExecutionContext, +} from '@jvalue/jayvee-execution/test'; +import { + type BlockDefinition, + IOType, + type JayveeServices, + createJayveeServices, +} from '@jvalue/jayvee-language-server'; +import { + type ParseHelperOptions, + expectNoParserAndLexerErrors, + loadTestExtensions, + parseHelper, + readJvTestAssetHelper, +} from '@jvalue/jayvee-language-server/test'; +import { + type AstNode, + type AstNodeLocator, + type LangiumDocument, +} from 'langium'; +import { NodeFileSystem } from 'langium/node'; +import type * as sqlite3 from 'sqlite3'; +import { type Mock, vi } from 'vitest'; + +import { SQLiteLoaderExecutor } from './sqlite-loader-executor'; + +type SqliteRunCallbackType = ( + result: sqlite3.RunResult, + err: Error | null, +) => void; +// eslint-disable-next-line no-var +var databaseMock: Mock; +// eslint-disable-next-line no-var +var databaseRunMock: Mock; +// eslint-disable-next-line no-var +var databaseCloseMock: Mock; +vi.mock('sqlite3', () => { + databaseMock = vi.fn(); + databaseRunMock = vi.fn(); + databaseCloseMock = vi.fn(); + return { + default: { + Database: databaseMock, + }, + }; +}); +function mockDatabaseDefault() { + const mockDB = { + close: databaseCloseMock, + run: databaseRunMock, + }; + databaseMock.mockImplementation(() => { + return mockDB; + }); +} + +describe('Validation of SQLiteLoaderExecutor', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + let locator: AstNodeLocator; + let services: JayveeServices; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../test/assets/sqlite-loader-executor/', + ); + + async function parseAndExecuteExecutor( + input: string, + IOInput: R.Table, + ): Promise<R.Result<R.None>> { + const document = await parse(input, { validation: true }); + expectNoParserAndLexerErrors(document); + + const block = locator.getAstNode<BlockDefinition>( + document.parseResult.value, + 'pipelines@0/blocks@1', + ) as BlockDefinition; + + return new SQLiteLoaderExecutor().doExecute( + IOInput, + getTestExecutionContext(locator, document, services, [block]), + ); + } + + beforeAll(async () => { + // Create language services + services = createJayveeServices(NodeFileSystem).Jayvee; + await loadTestExtensions(services, [ + path.resolve(__dirname, '../../test/test-extension/TestBlockTypes.jv'), + ]); + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + afterEach(() => { + vi.clearAllMocks(); + }); + + it('should diagnose no error on valid loader config', async () => { + mockDatabaseDefault(); + databaseRunMock.mockImplementation( + (sql: string, callback: SqliteRunCallbackType) => { + callback( + { + lastID: 0, + changes: 0, + } as sqlite3.RunResult, + null, + ); + return this; + }, + ); + const text = readJvTestAsset('valid-sqlite-loader.jv'); + + const inputTable = constructTable( + [ + { + columnName: 'Column1', + column: { + values: ['value 1'], + valueType: services.ValueTypeProvider.Primitives.Text, + }, + }, + { + columnName: 'Column2', + column: { + values: [20.2], + valueType: services.ValueTypeProvider.Primitives.Decimal, + }, + }, + ], + 1, + ); + const result = await parseAndExecuteExecutor(text, inputTable); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.NONE); + expect(databaseRunMock).toBeCalledTimes(3); + expect(databaseRunMock).nthCalledWith( + 1, + 'DROP TABLE IF EXISTS "Test";', + expect.any(Function), + ); + expect(databaseRunMock).nthCalledWith( + 2, + `CREATE TABLE IF NOT EXISTS "Test" ("Column1" text,"Column2" real);`, + expect.any(Function), + ); + expect(databaseRunMock).nthCalledWith( + 3, + `INSERT INTO "Test" ("Column1","Column2") VALUES ('value 1',20.2)`, + expect.any(Function), + ); + expect(databaseCloseMock).toBeCalledTimes(1); + } + }); + + it('should diagnose error on sqlite database open error', async () => { + databaseMock.mockImplementation(() => { + throw new Error('File not found'); + }); + const text = readJvTestAsset('valid-sqlite-loader.jv'); + + const inputTable = constructTable( + [ + { + columnName: 'Column1', + column: { + values: ['value 1'], + valueType: services.ValueTypeProvider.Primitives.Text, + }, + }, + { + columnName: 'Column2', + column: { + values: [20.2], + valueType: services.ValueTypeProvider.Primitives.Decimal, + }, + }, + ], + 1, + ); + const result = await parseAndExecuteExecutor(text, inputTable); + + expect(R.isOk(result)).toEqual(false); + if (R.isErr(result)) { + expect(result.left.message).toEqual( + 'Could not write to sqlite database: File not found', + ); + expect(databaseRunMock).toBeCalledTimes(0); + expect(databaseCloseMock).toBeCalledTimes(0); + } + }); +}); diff --git a/libs/extensions/rdbms/exec/src/lib/sqlite-loader-executor.ts b/libs/extensions/rdbms/exec/src/lib/sqlite-loader-executor.ts new file mode 100644 index 00000000..40eb9796 --- /dev/null +++ b/libs/extensions/rdbms/exec/src/lib/sqlite-loader-executor.ts @@ -0,0 +1,98 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import * as R from '@jvalue/jayvee-execution'; +import { + AbstractBlockExecutor, + type BlockExecutorClass, + type ExecutionContext, + NONE, + type None, + Table, + implementsStatic, +} from '@jvalue/jayvee-execution'; +import { IOType } from '@jvalue/jayvee-language-server'; +import sqlite3 from 'sqlite3'; + +@implementsStatic<BlockExecutorClass>() +export class SQLiteLoaderExecutor extends AbstractBlockExecutor< + IOType.TABLE, + IOType.NONE +> { + public static readonly type = 'SQLiteLoader'; + + constructor() { + super(IOType.TABLE, IOType.NONE); + } + + async doExecute( + input: Table, + context: ExecutionContext, + ): Promise<R.Result<None>> { + const file = context.getPropertyValue( + 'file', + context.valueTypeProvider.Primitives.Text, + ); + const table = context.getPropertyValue( + 'table', + context.valueTypeProvider.Primitives.Text, + ); + const dropTable = context.getPropertyValue( + 'dropTable', + context.valueTypeProvider.Primitives.Boolean, + ); + + let db: sqlite3.Database | undefined; + + try { + context.logger.logDebug(`Opening database file ${file}`); + db = new sqlite3.Database(file); + + if (dropTable) { + context.logger.logDebug( + `Dropping previous table "${table}" if it exists`, + ); + await this.runQuery(db, Table.generateDropTableStatement(table)); + } + + context.logger.logDebug(`Creating table "${table}"`); + await this.runQuery( + db, + input.generateCreateTableStatement(table, context), + ); + context.logger.logDebug( + `Inserting ${input.getNumberOfRows()} row(s) into table "${table}"`, + ); + await this.runQuery( + db, + input.generateInsertValuesStatement(table, context), + ); + + context.logger.logDebug( + `The data was successfully loaded into the database`, + ); + return R.ok(NONE); + } catch (err: unknown) { + return R.err({ + message: `Could not write to sqlite database: ${ + err instanceof Error ? err.message : JSON.stringify(err) + }`, + diagnostic: { node: context.getCurrentNode(), property: 'name' }, + }); + } finally { + db?.close(); + } + } + + private async runQuery( + db: sqlite3.Database, + query: string, + ): Promise<sqlite3.RunResult> { + return new Promise((resolve, reject) => { + db.run(query, (result: sqlite3.RunResult, error: Error | null) => + error ? reject(error) : resolve(result), + ); + }); + } +} diff --git a/libs/extensions/rdbms/exec/test/assets/postgres-loader-executor/valid-postgres-loader.jv b/libs/extensions/rdbms/exec/test/assets/postgres-loader-executor/valid-postgres-loader.jv new file mode 100644 index 00000000..93b769bc --- /dev/null +++ b/libs/extensions/rdbms/exec/test/assets/postgres-loader-executor/valid-postgres-loader.jv @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestBlock oftype PostgresLoader { + host: "localhost"; + port: 5432; + username: "postgres"; + password: "postgres"; + database: "TestDB"; + table: "Test"; + } + + TestExtractor -> TestBlock; +} diff --git a/libs/extensions/rdbms/exec/test/assets/sqlite-loader-executor/valid-sqlite-loader.jv b/libs/extensions/rdbms/exec/test/assets/sqlite-loader-executor/valid-sqlite-loader.jv new file mode 100644 index 00000000..3b9354b5 --- /dev/null +++ b/libs/extensions/rdbms/exec/test/assets/sqlite-loader-executor/valid-sqlite-loader.jv @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestBlock oftype SQLiteLoader { + table: "Test"; + file: "./test.db"; + } + + TestExtractor -> TestBlock; +} diff --git a/libs/extensions/rdbms/exec/test/index.ts b/libs/extensions/rdbms/exec/test/index.ts new file mode 100644 index 00000000..460aca6a --- /dev/null +++ b/libs/extensions/rdbms/exec/test/index.ts @@ -0,0 +1,5 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +export * from './mocks'; diff --git a/libs/extensions/rdbms/exec/test/mocks/index.ts b/libs/extensions/rdbms/exec/test/mocks/index.ts new file mode 100644 index 00000000..25746fea --- /dev/null +++ b/libs/extensions/rdbms/exec/test/mocks/index.ts @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +export * from './postgres-loader-executor-mock'; +export * from './sqlite-loader-executor-mock'; diff --git a/libs/extensions/rdbms/exec/test/mocks/postgres-loader-executor-mock.ts b/libs/extensions/rdbms/exec/test/mocks/postgres-loader-executor-mock.ts new file mode 100644 index 00000000..7e0d7d8a --- /dev/null +++ b/libs/extensions/rdbms/exec/test/mocks/postgres-loader-executor-mock.ts @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import assert from 'assert'; + +import { type BlockExecutorMock } from '@jvalue/jayvee-execution/test'; +import pg from 'pg'; +import { type Mock, type Mocked, vi } from 'vitest'; + +const { Client } = pg; // work around import issue with ESM + +type MockedPgClient = Mocked<typeof Client>; + +export class PostgresLoaderExecutorMock implements BlockExecutorMock { + private _pgClient: MockedPgClient | undefined; + + get pgClient(): MockedPgClient { + assert( + this._pgClient !== undefined, + 'Client not initialized - please call setup() first!', + ); + return this._pgClient; + } + + setup( + registerMocks: ( + pgClient: MockedPgClient, + ) => void = defaultPostgresMockRegistration, + ) { + // setup pg mock + this._pgClient = new Client() as unknown as MockedPgClient; + registerMocks(this._pgClient); + } + restore() { + // cleanup pg mock + vi.clearAllMocks(); + } +} + +export function defaultPostgresMockRegistration(pgClient: MockedPgClient) { + (pgClient.query as Mock).mockResolvedValue('Success'); +} diff --git a/libs/extensions/rdbms/exec/test/mocks/sqlite-loader-executor-mock.ts b/libs/extensions/rdbms/exec/test/mocks/sqlite-loader-executor-mock.ts new file mode 100644 index 00000000..c7e80a1e --- /dev/null +++ b/libs/extensions/rdbms/exec/test/mocks/sqlite-loader-executor-mock.ts @@ -0,0 +1,47 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import assert from 'assert'; + +import { type BlockExecutorMock } from '@jvalue/jayvee-execution/test'; +import sqlite3 from 'sqlite3'; +import { type Mock, type Mocked, vi } from 'vitest'; + +type MockedSqlite3Database = Mocked<sqlite3.Database>; + +export class SQLiteLoaderExecutorMock implements BlockExecutorMock { + private _sqliteClient: MockedSqlite3Database | undefined; + + get sqliteClient(): MockedSqlite3Database { + assert( + this._sqliteClient !== undefined, + 'Client not initialized - please call setup() first!', + ); + return this._sqliteClient; + } + + setup( + registerMocks: ( + sqliteClient: MockedSqlite3Database, + ) => void = defaultSQLiteMockRegistration, + ) { + // setup sqlite3 mock + this._sqliteClient = new sqlite3.Database('test') as MockedSqlite3Database; + registerMocks(this._sqliteClient); + } + restore() { + // cleanup sqlite3 mock + vi.clearAllMocks(); + } +} + +export function defaultSQLiteMockRegistration( + sqliteClient: MockedSqlite3Database, +) { + (sqliteClient.run as Mock).mockImplementation( + (query: string, callback: (result: unknown, err: Error | null) => void) => + callback('Success', null), + ); +} diff --git a/libs/extensions/rdbms/exec/test/test-extension/TestBlockTypes.jv b/libs/extensions/rdbms/exec/test/test-extension/TestBlockTypes.jv new file mode 100644 index 00000000..e050777a --- /dev/null +++ b/libs/extensions/rdbms/exec/test/test-extension/TestBlockTypes.jv @@ -0,0 +1,8 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +builtin blocktype TestTableExtractor { + input inPort oftype None; + output outPort oftype Table; +} diff --git a/libs/extensions/rdbms/exec/tsconfig.json b/libs/extensions/rdbms/exec/tsconfig.json new file mode 100644 index 00000000..d2cccb9f --- /dev/null +++ b/libs/extensions/rdbms/exec/tsconfig.json @@ -0,0 +1,21 @@ +{ + "extends": "../../../../tsconfig.base.json", + "compilerOptions": { + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/libs/extensions/rdbms/exec/tsconfig.json.license b/libs/extensions/rdbms/exec/tsconfig.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/rdbms/exec/tsconfig.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/rdbms/exec/tsconfig.lib.json b/libs/extensions/rdbms/exec/tsconfig.lib.json new file mode 100644 index 00000000..6e6931cb --- /dev/null +++ b/libs/extensions/rdbms/exec/tsconfig.lib.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../../dist/out-tsc", + "declaration": true, + "types": ["node"] + }, + "include": ["src/**/*.ts"], + "exclude": ["vite.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts", "test/**/*.ts"] +} diff --git a/libs/extensions/rdbms/exec/tsconfig.lib.json.license b/libs/extensions/rdbms/exec/tsconfig.lib.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/rdbms/exec/tsconfig.lib.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/rdbms/exec/tsconfig.spec.json b/libs/extensions/rdbms/exec/tsconfig.spec.json new file mode 100644 index 00000000..1ddf3dc6 --- /dev/null +++ b/libs/extensions/rdbms/exec/tsconfig.spec.json @@ -0,0 +1,27 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../../dist/out-tsc", + "types": [ + "vitest/globals", + "vitest/importMeta", + "vite/client", + "node", + "vitest" + ] + }, + "include": [ + "vite.config.ts", + "vitest.config.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.test.tsx", + "src/**/*.spec.tsx", + "src/**/*.test.js", + "src/**/*.spec.js", + "src/**/*.test.jsx", + "src/**/*.spec.jsx", + "src/**/*.d.ts", + "test/**/*.ts" + ] +} diff --git a/libs/extensions/rdbms/exec/tsconfig.spec.json.license b/libs/extensions/rdbms/exec/tsconfig.spec.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/rdbms/exec/tsconfig.spec.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/rdbms/exec/vite.config.ts b/libs/extensions/rdbms/exec/vite.config.ts new file mode 100644 index 00000000..5d3c77a6 --- /dev/null +++ b/libs/extensions/rdbms/exec/vite.config.ts @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// / <reference types='vitest' /> +import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + root: __dirname, + cacheDir: '../../../../node_modules/.vite/libs/extensions/rdbms/exec', + + plugins: [nxViteTsPaths()], + + test: { + globals: true, + cacheDir: '../../../../node_modules/.vitest', + environment: 'jsdom', + include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], + + reporters: ['default'], + coverage: { + reportsDirectory: '../../../../coverage/libs/extensions/rdbms/exec', + provider: 'v8', + }, + }, +}); diff --git a/libs/extensions/std/exec/.babelrc b/libs/extensions/std/exec/.babelrc new file mode 100644 index 00000000..fd4cbcde --- /dev/null +++ b/libs/extensions/std/exec/.babelrc @@ -0,0 +1,10 @@ +{ + "presets": [ + [ + "@nx/js/babel", + { + "useBuiltIns": "usage" + } + ] + ] +} diff --git a/libs/extensions/std/exec/.babelrc.license b/libs/extensions/std/exec/.babelrc.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/std/exec/.babelrc.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/std/exec/.eslintrc.json b/libs/extensions/std/exec/.eslintrc.json new file mode 100644 index 00000000..47701c9e --- /dev/null +++ b/libs/extensions/std/exec/.eslintrc.json @@ -0,0 +1,21 @@ +{ + "extends": ["../../../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "parserOptions": { + "project": ["libs/extensions/std/exec/tsconfig.lib.json", "libs/extensions/std/exec/tsconfig.spec.json"] + }, + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/libs/extensions/std/exec/.eslintrc.json.license b/libs/extensions/std/exec/.eslintrc.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/std/exec/.eslintrc.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/std/exec/README.md b/libs/extensions/std/exec/README.md new file mode 100644 index 00000000..03ac5016 --- /dev/null +++ b/libs/extensions/std/exec/README.md @@ -0,0 +1,17 @@ +<!-- +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only +--> + +# extensions-std-exec + +This library was generated with [Nx](https://nx.dev). + +## Building + +Run `nx build extensions-std-exec` to build the library. + +## Running unit tests + +Run `nx test extensions-std-exec` to execute the unit tests via [vitest](https://vitest.dev). diff --git a/libs/extensions/std/exec/package-lock.json b/libs/extensions/std/exec/package-lock.json new file mode 100644 index 00000000..93b6c39f --- /dev/null +++ b/libs/extensions/std/exec/package-lock.json @@ -0,0 +1,29 @@ +{ + "name": "@jvalue/jayvee-extensions/std/exec", + "version": "0.0.1", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "@jvalue/jayvee-extensions/std/exec", + "version": "0.0.1", + "devDependencies": { + "@types/mime-types": "^2.1.1" + } + }, + "node_modules/@types/mime-types": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.1.tgz", + "integrity": "sha512-vXOTGVSLR2jMw440moWTC7H19iUyLtP3Z1YTj7cSsubOICinjMxFeb/V57v9QdyyPGbbWolUFSSmSiRSn94tFw==", + "dev": true + } + }, + "dependencies": { + "@types/mime-types": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.1.tgz", + "integrity": "sha512-vXOTGVSLR2jMw440moWTC7H19iUyLtP3Z1YTj7cSsubOICinjMxFeb/V57v9QdyyPGbbWolUFSSmSiRSn94tFw==", + "dev": true + } + } +} diff --git a/libs/extensions/std/exec/package-lock.json.license b/libs/extensions/std/exec/package-lock.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/std/exec/package-lock.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/std/exec/package.json b/libs/extensions/std/exec/package.json new file mode 100644 index 00000000..547d7435 --- /dev/null +++ b/libs/extensions/std/exec/package.json @@ -0,0 +1,3 @@ +{ + "name": "@jvalue/jayvee-extensions/std/exec" +} diff --git a/libs/extensions/std/exec/package.json.license b/libs/extensions/std/exec/package.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/std/exec/package.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/std/exec/project.json b/libs/extensions/std/exec/project.json new file mode 100644 index 00000000..b422bc6a --- /dev/null +++ b/libs/extensions/std/exec/project.json @@ -0,0 +1,22 @@ +{ + "name": "extensions-std-exec", + "$schema": "../../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/extensions/std/exec/src", + "projectType": "library", + "targets": { + "build": {}, + "lint": { + "executor": "@nx/eslint:lint", + "outputs": ["{options.outputFile}"] + }, + "test": { + "executor": "@nx/vite:test", + "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], + "options": { + "configFile": "{projectRoot}/vite.config.ts", + "passWithNoTests": false + } + } + }, + "tags": [] +} diff --git a/libs/extensions/std/exec/project.json.license b/libs/extensions/std/exec/project.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/std/exec/project.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/std/exec/src/archive-interpreter-executor.spec.ts b/libs/extensions/std/exec/src/archive-interpreter-executor.spec.ts new file mode 100644 index 00000000..73c3860f --- /dev/null +++ b/libs/extensions/std/exec/src/archive-interpreter-executor.spec.ts @@ -0,0 +1,164 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import path from 'node:path'; + +import * as R from '@jvalue/jayvee-execution'; +import { + createBinaryFileFromLocalFile, + getTestExecutionContext, +} from '@jvalue/jayvee-execution/test'; +import { + type BlockDefinition, + IOType, + type JayveeServices, + createJayveeServices, +} from '@jvalue/jayvee-language-server'; +import { + type ParseHelperOptions, + expectNoParserAndLexerErrors, + loadTestExtensions, + parseHelper, + readJvTestAssetHelper, +} from '@jvalue/jayvee-language-server/test'; +import { + type AstNode, + type AstNodeLocator, + type LangiumDocument, +} from 'langium'; +import { NodeFileSystem } from 'langium/node'; + +import { ArchiveInterpreterExecutor } from './archive-interpreter-executor'; + +describe('Validation of ArchiveInterpreterExecutor', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + let locator: AstNodeLocator; + let services: JayveeServices; + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../test/assets/archive-interpreter-executor/', + ); + + function readTestArchive(fileName: string): R.BinaryFile { + const absoluteFileName = path.resolve( + __dirname, + '../test/assets/archive-interpreter-executor/', + fileName, + ); + return createBinaryFileFromLocalFile(absoluteFileName); + } + + async function parseAndExecuteArchiveInterpreter( + input: string, + IOInput: R.BinaryFile, + ): Promise<R.Result<R.FileSystem>> { + const document = await parse(input, { validation: true }); + expectNoParserAndLexerErrors(document); + + const block = locator.getAstNode<BlockDefinition>( + document.parseResult.value, + 'pipelines@0/blocks@1', + ) as BlockDefinition; + + return new ArchiveInterpreterExecutor().doExecute( + IOInput, + getTestExecutionContext(locator, document, services, [block]), + ); + } + + beforeAll(async () => { + // Create language services + services = createJayveeServices(NodeFileSystem).Jayvee; + await loadTestExtensions(services, [ + path.resolve(__dirname, '../test/test-extension/TestBlockTypes.jv'), + ]); + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + it('should diagnose no error on valid zip BinaryFile', async () => { + const text = readJvTestAsset('valid-zip-archive-interpreter.jv'); + + const testFile = readTestArchive('valid-zip.zip'); + const result = await parseAndExecuteArchiveInterpreter(text, testFile); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.FILE_SYSTEM); + expect(result.right.getFile('/test.txt')).toEqual( + expect.objectContaining({ + name: 'test.txt', + extension: 'txt', + ioType: IOType.FILE, + mimeType: R.MimeType.TEXT_PLAIN, + }), + ); + } + }); + + it('should diagnose no error on valid gz BinaryFile', async () => { + const text = readJvTestAsset('valid-gz-archive-interpreter.jv'); + + const testFile = readTestArchive('valid-gz.gz'); + const result = await parseAndExecuteArchiveInterpreter(text, testFile); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.FILE_SYSTEM); + expect(result.right.getFile('/valid-gz')).toEqual( + expect.objectContaining({ + name: 'valid-gz', + extension: '', + ioType: IOType.FILE, + mimeType: R.MimeType.APPLICATION_OCTET_STREAM, + }), + ); + } + }); + + it('should diagnose error on zip as gz archive', async () => { + const text = readJvTestAsset('valid-gz-archive-interpreter.jv'); + + const testFile = readTestArchive('valid-zip.zip'); + const result = await parseAndExecuteArchiveInterpreter(text, testFile); + + expect(R.isOk(result)).toEqual(false); + if (R.isErr(result)) { + expect(result.left.message).toEqual( + 'Unexpected Error incorrect header check occured during processing', + ); + } + }); + + it('should diagnose error on invalid archive type', async () => { + const text = readJvTestAsset('valid-7z-archive-interpreter.jv'); + + const testFile = readTestArchive('valid-7z.7z'); + const result = await parseAndExecuteArchiveInterpreter(text, testFile); + + expect(R.isOk(result)).toEqual(false); + if (R.isErr(result)) { + expect(result.left.message).toEqual('Archive type is not supported'); + } + }); + + it('should diagnose error on corrupted archive', async () => { + const text = readJvTestAsset('valid-zip-archive-interpreter.jv'); + + const testFile = readTestArchive('invalid-corrupt-zip.zip'); + const result = await parseAndExecuteArchiveInterpreter(text, testFile); + + expect(R.isOk(result)).toEqual(false); + if (R.isErr(result)) { + expect(result.left.message).toEqual( + "Unexpected Error Can't find end of central directory : is this a zip file ? If it is, see https://stuk.github.io/jszip/documentation/howto/read_zip.html occured during processing", + ); + } + }); +}); diff --git a/libs/extensions/std/exec/src/archive-interpreter-executor.ts b/libs/extensions/std/exec/src/archive-interpreter-executor.ts new file mode 100644 index 00000000..f3772412 --- /dev/null +++ b/libs/extensions/std/exec/src/archive-interpreter-executor.ts @@ -0,0 +1,150 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; +import path from 'node:path'; +import * as zlib from 'node:zlib'; + +import * as R from '@jvalue/jayvee-execution'; +import { + AbstractBlockExecutor, + BinaryFile, + type BlockExecutorClass, + type ExecutionContext, + FileExtension, + type FileSystem, + InMemoryFileSystem, + MimeType, + err, + implementsStatic, + inferFileExtensionFromFileExtensionString, + inferMimeTypeFromFileExtensionString, +} from '@jvalue/jayvee-execution'; +import { IOType } from '@jvalue/jayvee-language-server'; +import JSZip from 'jszip'; + +@implementsStatic<BlockExecutorClass>() +export class ArchiveInterpreterExecutor extends AbstractBlockExecutor< + IOType.FILE, + IOType.FILE_SYSTEM +> { + public static readonly type = 'ArchiveInterpreter'; + + constructor() { + super(IOType.FILE, IOType.FILE_SYSTEM); + } + + async doExecute( + archiveFile: BinaryFile, + context: ExecutionContext, + ): Promise<R.Result<FileSystem>> { + const archiveType = context.getPropertyValue( + 'archiveType', + context.valueTypeProvider.Primitives.Text, + ); + let fs: R.Result<R.FileSystem>; + + if (archiveType === 'zip') { + fs = await this.loadZipFileToInMemoryFileSystem(archiveFile, context); + } else if (archiveType === 'gz') { + fs = this.loadGzFileToInMemoryFileSystem(archiveFile, context); + } else { + return R.err({ + message: `Archive type is not supported`, + diagnostic: { node: context.getCurrentNode(), property: 'name' }, + }); + } + + if (R.isErr(fs)) { + return fs; + } + return R.ok(fs.right); + } + + private loadGzFileToInMemoryFileSystem( + archiveFile: BinaryFile, + context: ExecutionContext, + ): R.Result<FileSystem> { + context.logger.logDebug(`Loading gz file from binary content`); + try { + const fs = new InMemoryFileSystem(); + const archivedObject = zlib.gunzipSync(archiveFile.content); + + const extNameArchive = path.extname(archiveFile.name); + + const file = this.createFileFromArchive( + archiveFile.name, + archivedObject, + extNameArchive, + ); + + const addedFile = fs.putFile( + InMemoryFileSystem.getPathSeparator() + file.name, + file, + ); + assert(addedFile != null); + + return R.ok(fs); + } catch (error: unknown) { + return R.err(this.generateErrorObject(context, error)); + } + } + + private async loadZipFileToInMemoryFileSystem( + archiveFile: BinaryFile, + context: ExecutionContext, + ): Promise<R.Result<FileSystem>> { + context.logger.logDebug(`Loading zip file from binary content`); + try { + const jszip = JSZip(); + const fs = new InMemoryFileSystem(); + const archivedObjects = await jszip.loadAsync(archiveFile.content); + for (const [relPath, archivedObject] of Object.entries( + archivedObjects.files, + )) { + if (!archivedObject.dir) { + const content = await archivedObject.async('arraybuffer'); + + const file = this.createFileFromArchive(archivedObject.name, content); + + const addedFile = fs.putFile( + InMemoryFileSystem.getPathSeparator() + relPath, + file, + ); + assert(addedFile != null); + } + } + return R.ok(fs); + } catch (error: unknown) { + return R.err(this.generateErrorObject(context, error)); + } + } + + private createFileFromArchive( + archiveFileName: string, + content: ArrayBuffer, + extNameArchive?: string, + ) { + const fileName = path.basename(archiveFileName, extNameArchive); + const extName = path.extname(fileName); + + const mimeType = + inferMimeTypeFromFileExtensionString(extName) ?? + MimeType.APPLICATION_OCTET_STREAM; + const fileExtension = + inferFileExtensionFromFileExtensionString(extName) ?? FileExtension.NONE; + + return new BinaryFile(fileName, fileExtension, mimeType, content); + } + + private generateErrorObject(context: ExecutionContext, error: unknown) { + return { + message: `Unexpected Error ${ + error instanceof Error ? error.message : JSON.stringify(err) + } occured during processing`, + diagnostic: { node: context.getCurrentNode(), property: 'name' }, + }; + } +} diff --git a/libs/extensions/std/exec/src/extension.ts b/libs/extensions/std/exec/src/extension.ts new file mode 100644 index 00000000..7f0d3e79 --- /dev/null +++ b/libs/extensions/std/exec/src/extension.ts @@ -0,0 +1,40 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type BlockExecutorClass, + JayveeExecExtension, +} from '@jvalue/jayvee-execution'; +import { RdbmsExecExtension } from '@jvalue/jayvee-extensions/rdbms/exec'; +import { TabularExecExtension } from '@jvalue/jayvee-extensions/tabular/exec'; + +import { ArchiveInterpreterExecutor } from './archive-interpreter-executor'; +import { FilePickerExecutor } from './file-picker-executor'; +import { GtfsRTInterpreterExecutor } from './gtfs-rt-interpreter-executor'; +import { HttpExtractorExecutor } from './http-extractor-executor'; +import { LocalFileExtractorExecutor } from './local-file-extractor-executor'; +import { TextFileInterpreterExecutor } from './text-file-interpreter-executor'; +import { TextLineDeleterExecutor } from './text-line-deleter-executor'; +import { TextRangeSelectorExecutor } from './text-range-selector-executor'; + +export class StdExecExtension extends JayveeExecExtension { + private readonly wrappedExtensions: JayveeExecExtension[] = [ + new TabularExecExtension(), + new RdbmsExecExtension(), + ]; + + getBlockExecutors(): BlockExecutorClass[] { + return [ + ...this.wrappedExtensions.map((x) => x.getBlockExecutors()).flat(), + HttpExtractorExecutor, + TextFileInterpreterExecutor, + TextRangeSelectorExecutor, + TextLineDeleterExecutor, + ArchiveInterpreterExecutor, + FilePickerExecutor, + GtfsRTInterpreterExecutor, + LocalFileExtractorExecutor, + ]; + } +} diff --git a/libs/extensions/std/exec/src/file-picker-executor.spec.ts b/libs/extensions/std/exec/src/file-picker-executor.spec.ts new file mode 100644 index 00000000..0acae962 --- /dev/null +++ b/libs/extensions/std/exec/src/file-picker-executor.spec.ts @@ -0,0 +1,142 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import path from 'node:path'; + +import * as R from '@jvalue/jayvee-execution'; +import { + createBinaryFileFromLocalFile, + getTestExecutionContext, +} from '@jvalue/jayvee-execution/test'; +import { + type BlockDefinition, + IOType, + type JayveeServices, + createJayveeServices, +} from '@jvalue/jayvee-language-server'; +import { + type ParseHelperOptions, + expectNoParserAndLexerErrors, + loadTestExtensions, + parseHelper, + readJvTestAssetHelper, +} from '@jvalue/jayvee-language-server/test'; +import { + type AstNode, + type AstNodeLocator, + type LangiumDocument, +} from 'langium'; +import { NodeFileSystem } from 'langium/node'; + +import { FilePickerExecutor } from './file-picker-executor'; + +describe('Validation of FilePickerExecutor', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + let fileSystem: R.FileSystem; + + let locator: AstNodeLocator; + let services: JayveeServices; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../test/assets/file-picker-executor/', + ); + + function uploadTestFile(fileName: string) { + const absoluteFileName = path.resolve( + __dirname, + '../test/assets/file-picker-executor/', + fileName, + ); + fileSystem.putFile( + `/${fileName}`, + createBinaryFileFromLocalFile(absoluteFileName), + ); + } + + async function parseAndExecuteArchiveInterpreter( + input: string, + IOInput: R.FileSystem, + ): Promise<R.Result<R.BinaryFile | null>> { + const document = await parse(input, { validation: true }); + expectNoParserAndLexerErrors(document); + + const block = locator.getAstNode<BlockDefinition>( + document.parseResult.value, + 'pipelines@0/blocks@1', + ) as BlockDefinition; + + return new FilePickerExecutor().doExecute( + IOInput, + getTestExecutionContext(locator, document, services, [block]), + ); + } + + beforeAll(async () => { + // Create language services + services = createJayveeServices(NodeFileSystem).Jayvee; + await loadTestExtensions(services, [ + path.resolve(__dirname, '../test/test-extension/TestBlockTypes.jv'), + ]); + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + beforeEach(() => { + // Create fresh filesystem + fileSystem = new R.InMemoryFileSystem(); + }); + + it('should diagnose no error on valid txt file picker', async () => { + const text = readJvTestAsset('valid-file-picker.jv'); + uploadTestFile('test.txt'); + + const result = await parseAndExecuteArchiveInterpreter(text, fileSystem); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right).toEqual( + expect.objectContaining({ + name: 'test.txt', + extension: 'txt', + ioType: IOType.FILE, + mimeType: R.MimeType.TEXT_PLAIN, + }), + ); + } + }); + + it('should diagnose error on file not found', async () => { + const text = readJvTestAsset('valid-file-picker.jv'); + + const result = await parseAndExecuteArchiveInterpreter(text, fileSystem); + + expect(R.isOk(result)).toEqual(false); + if (R.isErr(result)) { + expect(result.left.message).toEqual(`File '/test.txt' not found`); + } + }); + + it('should work if the filename starts with a dot', async () => { + const text = readJvTestAsset('dot-valid-file-picker.jv'); + uploadTestFile('test.txt'); + + const result = await parseAndExecuteArchiveInterpreter(text, fileSystem); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right).toEqual( + expect.objectContaining({ + name: 'test.txt', + extension: 'txt', + ioType: IOType.FILE, + mimeType: R.MimeType.TEXT_PLAIN, + }), + ); + } + }); +}); diff --git a/libs/extensions/std/exec/src/file-picker-executor.ts b/libs/extensions/std/exec/src/file-picker-executor.ts new file mode 100644 index 00000000..8f7f6afc --- /dev/null +++ b/libs/extensions/std/exec/src/file-picker-executor.ts @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; + +import * as R from '@jvalue/jayvee-execution'; +import { + AbstractBlockExecutor, + BinaryFile, + type BlockExecutorClass, + type ExecutionContext, + type FileSystem, + implementsStatic, +} from '@jvalue/jayvee-execution'; +import { IOType } from '@jvalue/jayvee-language-server'; + +@implementsStatic<BlockExecutorClass>() +export class FilePickerExecutor extends AbstractBlockExecutor< + IOType.FILE_SYSTEM, + IOType.FILE +> { + public static readonly type = 'FilePicker'; + + constructor() { + super(IOType.FILE_SYSTEM, IOType.FILE); + } + + // eslint-disable-next-line @typescript-eslint/require-await + async doExecute( + fileSystem: FileSystem, + context: ExecutionContext, + ): Promise<R.Result<BinaryFile | null>> { + const path = context.getPropertyValue( + 'path', + context.valueTypeProvider.Primitives.Text, + ); + const file = fileSystem.getFile(path); + if (file == null) { + return R.err({ + message: `File '${path}' not found`, + diagnostic: { node: context.getCurrentNode() }, + }); + } + assert(file instanceof BinaryFile); + return R.ok(file); + } +} diff --git a/libs/extensions/std/exec/src/gtfs-rt-interpreter-executor.spec.ts b/libs/extensions/std/exec/src/gtfs-rt-interpreter-executor.spec.ts new file mode 100644 index 00000000..150d0c5d --- /dev/null +++ b/libs/extensions/std/exec/src/gtfs-rt-interpreter-executor.spec.ts @@ -0,0 +1,178 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import path from 'node:path'; + +import * as R from '@jvalue/jayvee-execution'; +import { + createBinaryFileFromLocalFile, + getTestExecutionContext, +} from '@jvalue/jayvee-execution/test'; +import { + type BlockDefinition, + IOType, + type JayveeServices, + createJayveeServices, +} from '@jvalue/jayvee-language-server'; +import { + type ParseHelperOptions, + expectNoParserAndLexerErrors, + loadTestExtensions, + parseHelper, + readJvTestAssetHelper, +} from '@jvalue/jayvee-language-server/test'; +import { + type AstNode, + type AstNodeLocator, + type LangiumDocument, +} from 'langium'; +import { NodeFileSystem } from 'langium/node'; + +import { GtfsRTInterpreterExecutor } from './gtfs-rt-interpreter-executor'; + +describe('Validation of GtfsRTInterpreterExecutor', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + let locator: AstNodeLocator; + let services: JayveeServices; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../test/assets/gtfs-rt-interpreter-executor/', + ); + + function readTestFile(fileName: string): R.BinaryFile { + const absoluteFileName = path.resolve( + __dirname, + '../test/assets/gtfs-rt-interpreter-executor/gtfs/', + fileName, + ); + return createBinaryFileFromLocalFile(absoluteFileName); + } + + async function parseAndExecuteExecutor( + input: string, + IOInput: R.BinaryFile, + ): Promise<R.Result<R.Sheet>> { + const document = await parse(input, { validation: true }); + expectNoParserAndLexerErrors(document); + + const block = locator.getAstNode<BlockDefinition>( + document.parseResult.value, + 'pipelines@0/blocks@1', + ) as BlockDefinition; + + return new GtfsRTInterpreterExecutor().doExecute( + IOInput, + getTestExecutionContext(locator, document, services, [block]), + ); + } + + beforeAll(async () => { + // Create language services + services = createJayveeServices(NodeFileSystem).Jayvee; + await loadTestExtensions(services, [ + path.resolve(__dirname, '../test/test-extension/TestBlockTypes.jv'), + ]); + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + it('should diagnose no error on valid trip update gtfs file', async () => { + const text = readJvTestAsset('valid-trip-update-gtfs-interpreter.jv'); + + const testFile = readTestFile('valid-trip-update'); + const result = await parseAndExecuteExecutor(text, testFile); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.SHEET); + expect(result.right.getNumberOfColumns()).toEqual(10); + expect(result.right.getNumberOfRows()).toEqual(23); + expect(result.right.getData()[0]).toContain( + 'entity.trip_update.trip.trip_id', + ); + } + }); + + it('should diagnose no error on valid alert gtfs file', async () => { + const text = readJvTestAsset('valid-alerts-gtfs-interpreter.jv'); + + const testFile = readTestFile('valid-alerts.json'); + const result = await parseAndExecuteExecutor(text, testFile); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.SHEET); + expect(result.right.getNumberOfColumns()).toEqual(7); + expect(result.right.getNumberOfRows()).toEqual(2); + expect(result.right.getData()[0]).toContain( + 'entity.alert.informed_entity.route_id', + ); + } + }); + + it('should diagnose no error on valid vehicle gtfs file', async () => { + const text = readJvTestAsset('valid-vehicle-gtfs-interpreter.jv'); + + const testFile = readTestFile('valid-vehicle'); + const result = await parseAndExecuteExecutor(text, testFile); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.SHEET); + expect(result.right.getNumberOfColumns()).toEqual(10); + expect(result.right.getNumberOfRows()).toEqual(2); + expect(result.right.getData()[0]).toContain( + 'entity.vehicle_position.vehicle_descriptor.id', + ); + } + }); + + it('should diagnose no error on wrong gtfs file for specified entity parameter', async () => { + const text = readJvTestAsset('valid-trip-update-gtfs-interpreter.jv'); + + const testFile = readTestFile('valid-alerts.json'); + const result = await parseAndExecuteExecutor(text, testFile); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.SHEET); + expect(result.right.getNumberOfColumns()).toEqual(10); + expect(result.right.getNumberOfRows()).toEqual(1); // only header row + } + }); + + it('should diagnose error on invalid entity parameter', async () => { + const text = readJvTestAsset('invalid-entity-parameter.jv'); + + const testFile = readTestFile('valid-trip-update'); + const result = await parseAndExecuteExecutor(text, testFile); + + expect(R.isOk(result)).toEqual(false); + if (R.isErr(result)) { + expect(result.left.message).toEqual( + 'Entity invalid not allowed for block GtfsRTInterpreterblock, expected "trip_update", "alert" or "vehicle".', + ); + } + }); + + it('should diagnose error on invalid gtfs file input', async () => { + const text = readJvTestAsset('invalid-entity-parameter.jv'); + + const testFile = readTestFile('invalid-gtfs'); + const result = await parseAndExecuteExecutor(text, testFile); + + expect(R.isOk(result)).toEqual(false); + if (R.isErr(result)) { + expect(result.left.message).toEqual( + 'Failed to decode gtfs file: invalid wire type 4 at offset 1', + ); + } + }); +}); diff --git a/libs/extensions/std/exec/src/gtfs-rt-interpreter-executor.ts b/libs/extensions/std/exec/src/gtfs-rt-interpreter-executor.ts new file mode 100644 index 00000000..3a351d7d --- /dev/null +++ b/libs/extensions/std/exec/src/gtfs-rt-interpreter-executor.ts @@ -0,0 +1,299 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import * as R from '@jvalue/jayvee-execution'; +import { + AbstractBlockExecutor, + type BinaryFile, + type BlockExecutorClass, + type ExecutionContext, + Sheet, + implementsStatic, +} from '@jvalue/jayvee-execution'; +import { IOType } from '@jvalue/jayvee-language-server'; +import { either as E } from 'fp-ts'; +import * as GtfsRealtimeBindings from 'gtfs-realtime-bindings'; + +@implementsStatic<BlockExecutorClass>() +export class GtfsRTInterpreterExecutor extends AbstractBlockExecutor< + IOType.FILE, + IOType.SHEET +> { + public static readonly type = 'GtfsRTInterpreter'; + + constructor() { + super(IOType.FILE, IOType.SHEET); + } + + async doExecute( + inputFile: BinaryFile, + context: ExecutionContext, + ): Promise<R.Result<Sheet>> { + // Accessing attribute values by their name: + const entity = context.getPropertyValue( + 'entity', + context.valueTypeProvider.Primitives.Text, + ); + + // https://github.com/MobilityData/gtfs-realtime-bindings/tree/master/nodejs + let feedMessage; + try { + feedMessage = GtfsRealtimeBindings.transit_realtime.FeedMessage.decode( + new Uint8Array(inputFile.content), + ); + } catch (e) { + return Promise.resolve( + R.err({ + message: `Failed to decode gtfs file: ${this.getErrorMessage(e)}`, + diagnostic: { node: context.getCurrentNode() }, + }), + ); + } + + // Parse all possible feedentity to Sheet + const sheet = await this.parseFeedMessage(entity, feedMessage, context); + if (E.isLeft(sheet)) { + return Promise.resolve( + R.err({ + message: sheet.left.message, + diagnostic: { node: context.getCurrentNode(), property: 'name' }, + }), + ); + } + return R.ok(sheet.right); + } + + private getErrorMessage(e: unknown): string { + if (e instanceof Error) { + return e.message; + } + return String(e); + } + + private async parseFeedMessage( + entityType: string, + feedMessage: GtfsRealtimeBindings.transit_realtime.FeedMessage, + context: ExecutionContext, + ): Promise<E.Either<Error, Sheet>> { + switch (entityType) { + case 'trip_update': + // Extract the trip updates from thee feed message + return this.parseTripUpdates(feedMessage, context); + case 'alert': + // Extract the alerts from thee feed message + return this.parseAlerts(feedMessage, context); + case 'vehicle': + // Extract vehicle positions from thee feed message + return this.parseVehiclePositions(feedMessage, context); + // Entity invalid + default: + return E.left( + new Error( + `Entity ${entityType} not allowed for block GtfsRTInterpreterblock, expected "trip_update", "alert" or "vehicle".`, + ), + ); + } + } + + private parseTripUpdates( + feedMessage: GtfsRealtimeBindings.transit_realtime.FeedMessage, + context: ExecutionContext, + ): Promise<E.Either<Error, Sheet>> { + return new Promise((resolve) => { + context.logger.logDebug(`Parsing raw gtfs-rt feed data as TripUpdate"`); + const rows: string[][] = []; + + // Add Header + rows.push([...tripUpdateHeader]); + + for (const entity of feedMessage.entity) { + // Case: No TripUpdates found -> return sheet just with header + if (!entity.tripUpdate) { + context.logger.logDebug( + `Parsing gtfs-rt feed data as TripUpdates: No Tripupdates found in feedmessage with timestamp "${String( + feedMessage.header.timestamp, + )}"`, + ); + break; + } + + // Case: No stopTimeUpdate found -> continue with next TripUpdate + if (!entity.tripUpdate.stopTimeUpdate) { + context.logger.logDebug( + `Parsing gtfs-rt feed data as TripUpdates: StopTimeUpdate of TripUpdate "${String( + entity.tripUpdate.trip.tripId, + )}" does not contain a single entry. Skipping this TripUpdate and continue with next one"`, + ); + continue; + } + + for (const stopTimeUpdate of entity.tripUpdate.stopTimeUpdate) { + const row: Record<TripUpdate, string> = { + 'header.gtfs_realtime_version': + feedMessage.header.gtfsRealtimeVersion, + 'header.timestamp': feedMessage.header.timestamp?.toString() ?? '', + 'header.incrementality': + feedMessage.header.incrementality?.toString() ?? '', + 'entity.id': entity.id, + 'entity.trip_update.trip.trip_id': + entity.tripUpdate.trip.tripId?.toString() ?? '', + 'entity.trip_update.trip.route_id': + entity.tripUpdate.trip.routeId?.toString() ?? '', + 'entity.trip_update.stop_time_update.stop_sequence': + stopTimeUpdate.stopSequence?.toString() ?? '', + 'entity.trip_update.stop_time_update.stop_id': + stopTimeUpdate.stopId?.toString() ?? '', + 'entity.trip_update.stop_time_update.arrival.time': + stopTimeUpdate.arrival?.time?.toString() ?? '', + 'entity.trip_update.stop_time_update.departure.time': + stopTimeUpdate.departure?.time?.toString() ?? '', + }; + rows.push(Object.values(row)); + } + } + resolve(E.right(new Sheet(rows))); + }); + } + + private parseVehiclePositions( + feedMessage: GtfsRealtimeBindings.transit_realtime.FeedMessage, + context: ExecutionContext, + ): Promise<E.Either<Error, Sheet>> { + return new Promise((resolve) => { + context.logger.logDebug( + `Parsing raw gtfs-rt feed data as VehiclePosition"`, + ); + + const rows: string[][] = []; + + // Add header + rows.push([...vehiclePositionHeader]); + + for (const entity of feedMessage.entity) { + // Case: No VehiclePositions found -> return sheet just with header + if (!entity.vehicle) { + context.logger.logDebug( + `Parsing gtfs-rt feed data as VehiclePosition: No VehiclePositions found in feedmessage with timestamp "${String( + feedMessage.header.timestamp, + )}"`, + ); + break; + } + const row: Record<VehiclePosition, string> = { + 'header.gtfs_realtime_version': + feedMessage.header.gtfsRealtimeVersion, + 'header.timestamp': feedMessage.header.timestamp?.toString() ?? '', + 'header.incrementality': + feedMessage.header.incrementality?.toString() ?? '', + 'entity.id': String(entity.id), + 'entity.vehicle_position.vehicle_descriptor.id': + entity.vehicle.vehicle?.id?.toString() ?? '', + 'entity.vehicle_position.trip.trip_id': + entity.vehicle.trip?.tripId?.toString() ?? '', + 'entity.vehicle_position.trip.route_id': + entity.vehicle.trip?.routeId?.toString() ?? '', + 'entity.vehicle_position.position.latitude': + entity.vehicle.position?.latitude.toString() ?? '', + 'entity.vehicle_position.position.longitude': + entity.vehicle.position?.longitude.toString() ?? '', + 'entity.vehicle_position.timestamp': + entity.vehicle.timestamp?.toString() ?? '', + }; + rows.push(Object.values(row)); + } + resolve(E.right(new Sheet(rows))); + }); + } + + private parseAlerts( + feedMessage: GtfsRealtimeBindings.transit_realtime.FeedMessage, + context: ExecutionContext, + ): Promise<E.Either<Error, Sheet>> { + return new Promise((resolve) => { + context.logger.logDebug(`Parsing raw gtfs-rt feed data as Alerts"`); + const rows: string[][] = []; + + // Add header + rows.push([...alertHeader]); + + for (const entity of feedMessage.entity) { + // Case: No Alerts found -> return sheet just with header + if (!entity.alert) { + context.logger.logDebug( + `Parsing gtfs-rt feed data as Alert: No Alerts found in feedmessage with timestamp "${String( + feedMessage.header.timestamp, + )}"`, + ); + break; + } + if (!entity.alert.informedEntity) { + context.logger.logDebug( + `Parsing gtfs-rt feed data as Alert: InformedEntity of Alert does not contain a single entry. Skipping this Alert and continue with next one"`, + ); + continue; + } + for (const informedEntity of entity.alert.informedEntity) { + const row: Record<Alert, string> = { + 'header.gtfs_realtime_version': + feedMessage.header.gtfsRealtimeVersion, + 'header.timestamp': feedMessage.header.timestamp?.toString() ?? '', + 'header.incrementality': + feedMessage.header.incrementality?.toString() ?? '', + 'entity.id': entity.id.toString(), + 'entity.alert.informed_entity.route_id': + informedEntity.routeId?.toString() ?? '', + 'entity.alert.header_text': + entity.alert.headerText?.translation?.shift()?.text.toString() ?? + '', + 'entity.alert.description_text': + entity.alert.descriptionText?.translation + ?.shift() + ?.text.toString() ?? '', + }; + rows.push(Object.values(row)); + } + } + resolve(E.right(new Sheet(rows))); + }); + } +} + +const tripUpdateHeader = [ + 'header.gtfs_realtime_version', + 'header.timestamp', + 'header.incrementality', + 'entity.id', + 'entity.trip_update.trip.trip_id', + 'entity.trip_update.trip.route_id', + 'entity.trip_update.stop_time_update.stop_sequence', + 'entity.trip_update.stop_time_update.stop_id', + 'entity.trip_update.stop_time_update.arrival.time', + 'entity.trip_update.stop_time_update.departure.time', +] as const; +type TripUpdate = (typeof tripUpdateHeader)[number]; + +const vehiclePositionHeader = [ + 'header.gtfs_realtime_version', + 'header.timestamp', + 'header.incrementality', + 'entity.id', + 'entity.vehicle_position.vehicle_descriptor.id', + 'entity.vehicle_position.trip.trip_id', + 'entity.vehicle_position.trip.route_id', + 'entity.vehicle_position.position.latitude', + 'entity.vehicle_position.position.longitude', + 'entity.vehicle_position.timestamp', +] as const; +type VehiclePosition = (typeof vehiclePositionHeader)[number]; + +const alertHeader = [ + 'header.gtfs_realtime_version', + 'header.timestamp', + 'header.incrementality', + 'entity.id', + 'entity.alert.informed_entity.route_id', + 'entity.alert.header_text', + 'entity.alert.description_text', +] as const; +type Alert = (typeof alertHeader)[number]; diff --git a/libs/extensions/std/exec/src/http-extractor-executor.spec.ts b/libs/extensions/std/exec/src/http-extractor-executor.spec.ts new file mode 100644 index 00000000..be6e4eb7 --- /dev/null +++ b/libs/extensions/std/exec/src/http-extractor-executor.spec.ts @@ -0,0 +1,224 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import path from 'node:path'; + +import * as R from '@jvalue/jayvee-execution'; +import { getTestExecutionContext } from '@jvalue/jayvee-execution/test'; +import { + type BlockDefinition, + IOType, + type JayveeServices, + createJayveeServices, +} from '@jvalue/jayvee-language-server'; +import { + type ParseHelperOptions, + expectNoParserAndLexerErrors, + loadTestExtensions, + parseHelper, + readJvTestAssetHelper, +} from '@jvalue/jayvee-language-server/test'; +import { + type AstNode, + type AstNodeLocator, + type LangiumDocument, +} from 'langium'; +import { NodeFileSystem } from 'langium/node'; +import nock from 'nock'; + +import { HttpExtractorExecutor } from './http-extractor-executor'; + +describe('Validation of HttpExtractorExecutor', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + let locator: AstNodeLocator; + let services: JayveeServices; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../test/assets/http-extractor-executor/', + ); + + async function parseAndExecuteExecutor( + input: string, + ): Promise<R.Result<R.BinaryFile>> { + const document = await parse(input, { validation: true }); + expectNoParserAndLexerErrors(document); + + const block = locator.getAstNode<BlockDefinition>( + document.parseResult.value, + 'pipelines@0/blocks@1', + ) as BlockDefinition; + + return new HttpExtractorExecutor().doExecute( + R.NONE, + getTestExecutionContext(locator, document, services, [block]), + ); + } + + beforeAll(async () => { + // Create language services + services = createJayveeServices(NodeFileSystem).Jayvee; + await loadTestExtensions(services, [ + path.resolve(__dirname, '../test/test-extension/TestBlockTypes.jv'), + ]); + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + afterEach(() => { + nock.restore(); + }); + + beforeEach(() => { + if (!nock.isActive()) { + nock.activate(); + } + nock.cleanAll(); + }); + + it('should diagnose no error on valid http url', async () => { + const text = readJvTestAsset('valid-http.jv'); + const nockScope = nock('http://localhost') + .get('/test.txt') + .replyWithFile( + 200, + path.resolve( + __dirname, + '../test/assets/http-extractor-executor/test.txt', + ), + { + 'Content-Type': 'text/plain', + }, + ); + + const result = await parseAndExecuteExecutor(text); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(nockScope.isDone()).toEqual(true); + expect(result.right).toEqual( + expect.objectContaining({ + name: 'test.txt', + extension: 'txt', + ioType: IOType.FILE, + mimeType: R.MimeType.APPLICATION_OCTET_STREAM, + }), + ); + } + }); + + it('should diagnose no error on valid https url', async () => { + const text = readJvTestAsset('valid-https.jv'); + const nockScope = nock('https://localhost') + .get('/test.txt') + .replyWithFile( + 200, + path.resolve( + __dirname, + '../test/assets/http-extractor-executor/test.txt', + ), + { + 'Content-Type': 'text/plain', + }, + ); + + const result = await parseAndExecuteExecutor(text); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(nockScope.isDone()).toEqual(true); + expect(result.right).toEqual( + expect.objectContaining({ + name: 'test.txt', + extension: 'txt', + ioType: IOType.FILE, + mimeType: R.MimeType.APPLICATION_OCTET_STREAM, + }), + ); + } + }); + + it('should diagnose no error on retry', async () => { + const text = readJvTestAsset('valid-one-retry.jv'); + const nockScope404 = nock('https://localhost').get('/test.txt').reply(404); + const nockScope200 = nock('https://localhost') + .get('/test.txt') + .replyWithFile( + 200, + path.resolve( + __dirname, + '../test/assets/http-extractor-executor/test.txt', + ), + { + 'Content-Type': 'text/plain', + }, + ); + + const result = await parseAndExecuteExecutor(text); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(nockScope404.isDone()).toEqual(true); + expect(nockScope200.isDone()).toEqual(true); + expect(result.right).toEqual( + expect.objectContaining({ + name: 'test.txt', + extension: 'txt', + ioType: IOType.FILE, + mimeType: R.MimeType.APPLICATION_OCTET_STREAM, + }), + ); + } + }); + + it('should diagnose error on url not found', async () => { + const text = readJvTestAsset('valid-http.jv'); + const nockScope = nock('http://localhost').get('/test.txt').reply(404); + + const result = await parseAndExecuteExecutor(text); + + expect(R.isOk(result)).toEqual(false); + if (R.isErr(result)) { + expect(nockScope.isDone()).toEqual(true); + expect(result.left.message).toEqual( + 'HTTP fetch failed with code 404. Please check your connection.', + ); + } + }); + + it('should diagnose error on ClientRequest error', async () => { + const text = readJvTestAsset('valid-https.jv'); + const nockScope = nock('https://localhost') + .get('/test.txt') + .replyWithError('Test error'); + + const result = await parseAndExecuteExecutor(text); + + expect(R.isOk(result)).toEqual(false); + if (R.isErr(result)) { + expect(nockScope.isDone()).toEqual(true); + expect(result.left.message).toEqual('Test error'); + } + }); + + it('should diagnose error on ignoring redirects', async () => { + const text = readJvTestAsset('valid-http.jv'); + const nockScope = nock('http://localhost').get('/test.txt').reply(301); + + const result = await parseAndExecuteExecutor(text); + + expect(R.isOk(result)).toEqual(false); + if (R.isErr(result)) { + expect(nockScope.isDone()).toEqual(true); + expect(result.left.message).toEqual( + 'HTTP fetch was redirected with code 301. Redirects are either disabled or maximum number of redirects was exeeded.', + ); + } + }); +}); diff --git a/libs/extensions/std/exec/src/http-extractor-executor.ts b/libs/extensions/std/exec/src/http-extractor-executor.ts new file mode 100644 index 00000000..ffada2a6 --- /dev/null +++ b/libs/extensions/std/exec/src/http-extractor-executor.ts @@ -0,0 +1,210 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; +import path from 'node:path'; + +import * as R from '@jvalue/jayvee-execution'; +import { + AbstractBlockExecutor, + BinaryFile, + type BlockExecutorClass, + type ExecutionContext, + type ExecutionErrorDetails, + FileExtension, + MimeType, + type None, + implementsStatic, + inferFileExtensionFromContentTypeString, + inferFileExtensionFromFileExtensionString, + inferMimeTypeFromFileExtensionString, +} from '@jvalue/jayvee-execution'; +import { IOType } from '@jvalue/jayvee-language-server'; +import followRedirects from 'follow-redirects'; +import { type AstNode } from 'langium'; + +import { + createBackoffStrategy, + isBackoffStrategyHandle, +} from './util/backoff-strategy'; + +const { http, https } = followRedirects; + +type HttpGetFunction = typeof http.get; + +@implementsStatic<BlockExecutorClass>() +export class HttpExtractorExecutor extends AbstractBlockExecutor< + IOType.NONE, + IOType.FILE +> { + public static readonly type = 'HttpExtractor'; + + constructor() { + super(IOType.NONE, IOType.FILE); + } + + async doExecute( + input: None, + context: ExecutionContext, + ): Promise<R.Result<BinaryFile>> { + const url = context.getPropertyValue( + 'url', + context.valueTypeProvider.Primitives.Text, + ); + const retries = context.getPropertyValue( + 'retries', + context.valueTypeProvider.Primitives.Integer, + ); + assert(retries >= 0); // loop executes at least once + const retryBackoffMilliseconds = context.getPropertyValue( + 'retryBackoffMilliseconds', + context.valueTypeProvider.Primitives.Integer, + ); + const retryBackoffStrategy = context.getPropertyValue( + 'retryBackoffStrategy', + context.valueTypeProvider.Primitives.Text, + ); + assert(isBackoffStrategyHandle(retryBackoffStrategy)); + const backoffStrategy = createBackoffStrategy( + retryBackoffStrategy, + retryBackoffMilliseconds, + ); + + let failure: ExecutionErrorDetails<AstNode> | undefined; + for (let attempt = 0; attempt <= retries; ++attempt) { + const isLastAttempt = attempt === retries; + const file = await this.fetchRawDataAsFile(url, context); + + if (R.isOk(file)) { + context.logger.logDebug(`Successfully fetched raw data`); + return R.ok(file.right); + } + + failure = file.left; + + if (!isLastAttempt) { + context.logger.logDebug(failure.message); + + const currentBackoff = backoffStrategy.getBackoffMilliseconds( + attempt + 1, + ); + context.logger.logDebug( + `Waiting ${currentBackoff}ms before trying again...`, + ); + await new Promise((p) => setTimeout(p, currentBackoff)); + continue; + } + } + + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + return R.err(failure!); + } + + private fetchRawDataAsFile( + url: string, + context: ExecutionContext, + ): Promise<R.Result<BinaryFile>> { + context.logger.logDebug(`Fetching raw data from ${url}`); + let httpGetFunction: HttpGetFunction; + if (url.startsWith('https')) { + httpGetFunction = https.get.bind(https); + } else { + httpGetFunction = http.get.bind(http); + } + const followRedirects = context.getPropertyValue( + 'followRedirects', + context.valueTypeProvider.Primitives.Boolean, + ); + return new Promise((resolve) => { + httpGetFunction(url, { followRedirects: followRedirects }, (response) => { + const responseCode = response.statusCode; + + // Catch errors + if (responseCode === undefined || responseCode >= 400) { + resolve( + R.err({ + message: `HTTP fetch failed with code ${ + responseCode ?? 'undefined' + }. Please check your connection.`, + diagnostic: { node: context.getOrFailProperty('url') }, + }), + ); + } else if (responseCode >= 301 && responseCode < 400) { + resolve( + R.err({ + message: `HTTP fetch was redirected with code ${responseCode}. Redirects are either disabled or maximum number of redirects was exeeded.`, + diagnostic: { node: context.getOrFailProperty('url') }, + }), + ); + } + + // Get chunked data and store to ArrayBuffer + let rawData = new Uint8Array(0); + response.on('data', (chunk: Buffer) => { + const tmp = new Uint8Array(rawData.length + chunk.length); + tmp.set(rawData, 0); + tmp.set(chunk, rawData.length); + rawData = tmp; + }); + + // When all data is downloaded, create file + response.on('end', () => { + response.headers; + + // Infer Mimetype from HTTP-Header, if not inferrable, then default to application/octet-stream + const mimeType: MimeType | undefined = + inferMimeTypeFromFileExtensionString( + response.headers['content-type'], + ) ?? MimeType.APPLICATION_OCTET_STREAM; + + // Infer FileName and FileExtension from url, if not inferrable, then default to None + // Get last element of URL assuming this is a filename + const url = new URL(response.responseUrl); + let fileName = url.pathname.split('/').pop(); + if (fileName === undefined) { + fileName = url.pathname.replace('/', '-'); + } + const extName = path.extname(fileName); + let fileExtension = + inferFileExtensionFromFileExtensionString(extName) ?? + FileExtension.NONE; + + // If FileExtension is not in url, try to infer extension from content-type, if not inferrable, then default to None + if (fileExtension === FileExtension.NONE) { + fileExtension = + inferFileExtensionFromContentTypeString( + response.headers['content-type'], + ) ?? FileExtension.NONE; + } + + // Create file and return file + const file = new BinaryFile( + fileName, + fileExtension, + mimeType, + rawData.buffer as ArrayBuffer, + ); + resolve(R.ok(file)); + }); + + response.on('error', (errorObj) => { + resolve( + R.err({ + message: errorObj.message, + diagnostic: { node: context.getCurrentNode(), property: 'name' }, + }), + ); + }); + }).on('error', (e: Error) => { + resolve( + R.err({ + message: e.message, + diagnostic: { node: context.getCurrentNode(), property: 'name' }, + }), + ); + }); + }); + } +} diff --git a/libs/extensions/std/exec/src/index.ts b/libs/extensions/std/exec/src/index.ts new file mode 100644 index 00000000..88d3a004 --- /dev/null +++ b/libs/extensions/std/exec/src/index.ts @@ -0,0 +1,5 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +export * from './extension'; diff --git a/libs/extensions/std/exec/src/local-file-extractor-executor.spec.ts b/libs/extensions/std/exec/src/local-file-extractor-executor.spec.ts new file mode 100644 index 00000000..b0620036 --- /dev/null +++ b/libs/extensions/std/exec/src/local-file-extractor-executor.spec.ts @@ -0,0 +1,137 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import path from 'node:path'; + +import * as R from '@jvalue/jayvee-execution'; +import { getTestExecutionContext } from '@jvalue/jayvee-execution/test'; +import { + type BlockDefinition, + IOType, + type JayveeServices, + createJayveeServices, +} from '@jvalue/jayvee-language-server'; +import { + expectNoParserAndLexerErrors, + loadTestExtensions, + parseHelper, + readJvTestAssetHelper, +} from '@jvalue/jayvee-language-server/test'; +import { + type AstNode, + type AstNodeLocator, + type LangiumDocument, +} from 'langium'; +import { NodeFileSystem } from 'langium/node'; +import * as nock from 'nock'; + +import { LocalFileExtractorExecutor } from './local-file-extractor-executor'; + +describe('Validation of LocalFileExtractorExecutor', () => { + let parse: (input: string) => Promise<LangiumDocument<AstNode>>; + + let locator: AstNodeLocator; + let services: JayveeServices; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../test/assets/local-file-extractor-executor/', + ); + + async function parseAndExecuteExecutor( + input: string, + ): Promise<R.Result<R.BinaryFile>> { + const document = await parse(input); + expectNoParserAndLexerErrors(document); + + const block = locator.getAstNode<BlockDefinition>( + document.parseResult.value, + 'pipelines@0/blocks@1', + ) as BlockDefinition; + + return new LocalFileExtractorExecutor().doExecute( + R.NONE, + getTestExecutionContext(locator, document, services, [block]), + ); + } + + beforeAll(async () => { + // Create language services + services = createJayveeServices(NodeFileSystem).Jayvee; + await loadTestExtensions(services, [ + path.resolve(__dirname, '../test/test-extension/TestBlockTypes.jv'), + ]); + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + afterEach(() => { + nock.restore(); + }); + + beforeEach(() => { + if (!nock.isActive()) { + nock.activate(); + } + nock.cleanAll(); + }); + + it('should diagnose no error on valid local file path', async () => { + const text = readJvTestAsset('valid-local-file.jv'); + + const result = await parseAndExecuteExecutor(text); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right).toEqual( + expect.objectContaining({ + name: 'local-file-test.csv', + extension: 'csv', + ioType: IOType.FILE, + mimeType: R.MimeType.TEXT_CSV, + }), + ); + } + }); + + it('should diagnose error on file not found', async () => { + const text = readJvTestAsset('invalid-file-not-found.jv'); + + const result = await parseAndExecuteExecutor(text); + + expect(R.isErr(result)).toEqual(true); + if (R.isErr(result)) { + expect(result.left.message).toEqual( + `File './does-not-exist.csv' not found.`, + ); + } + }); + + it('should diagnose error on path traversal at the start of the path', async () => { + const text = readJvTestAsset('invalid-path-traversal-at-start.jv'); + + const result = await parseAndExecuteExecutor(text); + + expect(R.isErr(result)).toEqual(true); + if (R.isErr(result)) { + expect(result.left.message).toEqual( + `File path cannot include "..". Path traversal is restricted.`, + ); + } + }); + + it('should diagnose error on path traversal in the path', async () => { + const text = readJvTestAsset('invalid-path-traversal-in-path.jv'); + + const result = await parseAndExecuteExecutor(text); + + expect(R.isErr(result)).toEqual(true); + if (R.isErr(result)) { + expect(result.left.message).toEqual( + `File path cannot include "..". Path traversal is restricted.`, + ); + } + }); +}); diff --git a/libs/extensions/std/exec/src/local-file-extractor-executor.ts b/libs/extensions/std/exec/src/local-file-extractor-executor.ts new file mode 100644 index 00000000..23ea6780 --- /dev/null +++ b/libs/extensions/std/exec/src/local-file-extractor-executor.ts @@ -0,0 +1,82 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import * as fs from 'node:fs/promises'; +import path from 'node:path'; + +import * as R from '@jvalue/jayvee-execution'; +import { + AbstractBlockExecutor, + BinaryFile, + type BlockExecutorClass, + type ExecutionContext, + FileExtension, + MimeType, + type None, + implementsStatic, + inferFileExtensionFromFileExtensionString, + inferMimeTypeFromFileExtensionString, +} from '@jvalue/jayvee-execution'; +import { IOType } from '@jvalue/jayvee-language-server'; + +@implementsStatic<BlockExecutorClass>() +export class LocalFileExtractorExecutor extends AbstractBlockExecutor< + IOType.NONE, + IOType.FILE +> { + public static readonly type = 'LocalFileExtractor'; + + constructor() { + super(IOType.NONE, IOType.FILE); + } + + async doExecute( + input: None, + context: ExecutionContext, + ): Promise<R.Result<BinaryFile>> { + const filePath = context.getPropertyValue( + 'filePath', + context.valueTypeProvider.Primitives.Text, + ); + + if (filePath.includes('..')) { + return R.err({ + message: 'File path cannot include "..". Path traversal is restricted.', + diagnostic: { node: context.getCurrentNode(), property: 'filePath' }, + }); + } + + try { + const rawData = await fs.readFile(filePath); + + // Infer FileName and FileExtension from filePath + const fileName = path.basename(filePath); + const extName = path.extname(fileName); + const fileExtension = + inferFileExtensionFromFileExtensionString(extName) ?? + FileExtension.NONE; + + // Infer Mimetype from FileExtension, if not inferrable, then default to application/octet-stream + const mimeType: MimeType | undefined = + inferMimeTypeFromFileExtensionString(fileExtension) ?? + MimeType.APPLICATION_OCTET_STREAM; + + // Create file and return file + const file = new BinaryFile( + fileName, + fileExtension, + mimeType, + rawData.buffer as ArrayBuffer, + ); + + context.logger.logDebug(`Successfully extraced file ${filePath}`); + return R.ok(file); + } catch (error) { + return R.err({ + message: `File '${filePath}' not found.`, + diagnostic: { node: context.getCurrentNode(), property: 'filePath' }, + }); + } + } +} diff --git a/libs/extensions/std/exec/src/text-file-interpreter-executor.spec.ts b/libs/extensions/std/exec/src/text-file-interpreter-executor.spec.ts new file mode 100644 index 00000000..2dcbe314 --- /dev/null +++ b/libs/extensions/std/exec/src/text-file-interpreter-executor.spec.ts @@ -0,0 +1,130 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import path from 'node:path'; + +import * as R from '@jvalue/jayvee-execution'; +import { + createBinaryFileFromLocalFile, + getTestExecutionContext, +} from '@jvalue/jayvee-execution/test'; +import { + type BlockDefinition, + IOType, + type JayveeServices, + createJayveeServices, +} from '@jvalue/jayvee-language-server'; +import { + type ParseHelperOptions, + expectNoParserAndLexerErrors, + loadTestExtensions, + parseHelper, + readJvTestAssetHelper, +} from '@jvalue/jayvee-language-server/test'; +import { + type AstNode, + type AstNodeLocator, + type LangiumDocument, +} from 'langium'; +import { NodeFileSystem } from 'langium/node'; + +import { TextFileInterpreterExecutor } from './text-file-interpreter-executor'; + +describe('Validation of TextFileInterpreterExecutor', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + let locator: AstNodeLocator; + let services: JayveeServices; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../test/assets/text-file-interpreter-executor/', + ); + + function readTestFile(fileName: string): R.BinaryFile { + const absoluteFileName = path.resolve( + __dirname, + '../test/assets/text-file-interpreter-executor/', + fileName, + ); + return createBinaryFileFromLocalFile(absoluteFileName); + } + + async function parseAndExecuteExecutor( + input: string, + IOInput: R.BinaryFile, + ): Promise<R.Result<R.TextFile>> { + const document = await parse(input, { validation: true }); + expectNoParserAndLexerErrors(document); + + const block = locator.getAstNode<BlockDefinition>( + document.parseResult.value, + 'pipelines@0/blocks@1', + ) as BlockDefinition; + + return new TextFileInterpreterExecutor().doExecute( + IOInput, + getTestExecutionContext(locator, document, services, [block]), + ); + } + + beforeAll(async () => { + // Create language services + services = createJayveeServices(NodeFileSystem).Jayvee; + await loadTestExtensions(services, [ + path.resolve(__dirname, '../test/test-extension/TestBlockTypes.jv'), + ]); + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + it('should diagnose no error on valid text file', async () => { + const text = readJvTestAsset('valid-default-file-interpreter.jv'); + + const testFile = readTestFile('test.txt'); + const result = await parseAndExecuteExecutor(text, testFile); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.TEXT_FILE); + expect(result.right.content).toEqual( + expect.arrayContaining(['Multiline ', 'Test File']), + ); + } + }); + + it('should diagnose no error on non text file', async () => { + const text = readJvTestAsset('valid-default-file-interpreter.jv'); + + const testFile = readTestFile('gtfs-vehicle'); + const result = await parseAndExecuteExecutor(text, testFile); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.TEXT_FILE); + expect(result.right.content).toEqual( + expect.arrayContaining(['vehicle:268435857"0']), + ); + } + }); + + it('should diagnose no error on custom lineBreak', async () => { + const text = readJvTestAsset('valid-custom-line-break.jv'); + + const testFile = readTestFile('test.txt'); + const result = await parseAndExecuteExecutor(text, testFile); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.TEXT_FILE); + expect(result.right.content).toEqual( + expect.arrayContaining(['Multiline \nTest', 'File\n']), + ); + } + }); +}); diff --git a/libs/extensions/std/exec/src/text-file-interpreter-executor.ts b/libs/extensions/std/exec/src/text-file-interpreter-executor.ts new file mode 100644 index 00000000..b7417507 --- /dev/null +++ b/libs/extensions/std/exec/src/text-file-interpreter-executor.ts @@ -0,0 +1,60 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { TextDecoder } from 'node:util'; + +import * as R from '@jvalue/jayvee-execution'; +import { + AbstractBlockExecutor, + type BinaryFile, + type BlockExecutorClass, + type ExecutionContext, + TextFile, + implementsStatic, + splitLines, +} from '@jvalue/jayvee-execution'; +import { IOType } from '@jvalue/jayvee-language-server'; + +@implementsStatic<BlockExecutorClass>() +export class TextFileInterpreterExecutor extends AbstractBlockExecutor< + IOType.FILE, + IOType.TEXT_FILE +> { + public static readonly type = 'TextFileInterpreter'; + + constructor() { + super(IOType.FILE, IOType.TEXT_FILE); + } + + // eslint-disable-next-line @typescript-eslint/require-await + async doExecute( + file: BinaryFile, + context: ExecutionContext, + ): Promise<R.Result<TextFile>> { + const encoding = context.getPropertyValue( + 'encoding', + context.valueTypeProvider.Primitives.Text, + ); + const lineBreak = context.getPropertyValue( + 'lineBreak', + context.valueTypeProvider.Primitives.Regex, + ); + + const decoder = new TextDecoder(encoding); + context.logger.logDebug( + `Decoding file content using encoding "${encoding}"`, + ); + const textContent = decoder.decode(file.content); + + context.logger.logDebug( + `Splitting lines using line break /${lineBreak.source}/`, + ); + const lines = splitLines(textContent, lineBreak); + context.logger.logDebug( + `Lines were split successfully, the resulting text file has ${lines.length} lines`, + ); + + return R.ok(new TextFile(file.name, file.extension, file.mimeType, lines)); + } +} diff --git a/libs/extensions/std/exec/src/text-line-deleter-executor.spec.ts b/libs/extensions/std/exec/src/text-line-deleter-executor.spec.ts new file mode 100644 index 00000000..adeb9158 --- /dev/null +++ b/libs/extensions/std/exec/src/text-line-deleter-executor.spec.ts @@ -0,0 +1,144 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import path from 'node:path'; + +import * as R from '@jvalue/jayvee-execution'; +import { + createTextFileFromLocalFile, + getTestExecutionContext, +} from '@jvalue/jayvee-execution/test'; +import { + type BlockDefinition, + IOType, + type JayveeServices, + createJayveeServices, +} from '@jvalue/jayvee-language-server'; +import { + type ParseHelperOptions, + expectNoParserAndLexerErrors, + loadTestExtensions, + parseHelper, + readJvTestAssetHelper, +} from '@jvalue/jayvee-language-server/test'; +import { + type AstNode, + type AstNodeLocator, + type LangiumDocument, +} from 'langium'; +import { NodeFileSystem } from 'langium/node'; + +import { TextLineDeleterExecutor } from './text-line-deleter-executor'; + +describe('Validation of TextLineDeleterExecutor', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + let locator: AstNodeLocator; + let services: JayveeServices; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../test/assets/text-line-deleter-executor/', + ); + + function readTestFile(fileName: string): R.TextFile { + const absoluteFileName = path.resolve( + __dirname, + '../test/assets/text-line-deleter-executor/', + fileName, + ); + return createTextFileFromLocalFile(absoluteFileName); + } + + async function parseAndExecuteExecutor( + input: string, + IOInput: R.TextFile, + ): Promise<R.Result<R.TextFile>> { + const document = await parse(input, { validation: true }); + expectNoParserAndLexerErrors(document); + + const block = locator.getAstNode<BlockDefinition>( + document.parseResult.value, + 'pipelines@0/blocks@1', + ) as BlockDefinition; + + return new TextLineDeleterExecutor().doExecute( + IOInput, + getTestExecutionContext(locator, document, services, [block]), + ); + } + + beforeAll(async () => { + // Create language services + services = createJayveeServices(NodeFileSystem).Jayvee; + await loadTestExtensions(services, [ + path.resolve(__dirname, '../test/test-extension/TestBlockTypes.jv'), + ]); + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + it('should diagnose no error on valid text file with at least one line', async () => { + const text = readJvTestAsset('valid-first-line.jv'); + + const testFile = readTestFile('test.txt'); + const result = await parseAndExecuteExecutor(text, testFile); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.TEXT_FILE); + expect(result.right.content).toEqual( + expect.arrayContaining(['Test File']), + ); + } + }); + + it('should diagnose no error on empty lines parameter', async () => { + const text = readJvTestAsset('valid-no-line.jv'); + + const testFile = readTestFile('test.txt'); + const result = await parseAndExecuteExecutor(text, testFile); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.TEXT_FILE); + expect(result.right.content).toEqual( + expect.arrayContaining(['Multiline', 'Test File']), + ); + } + }); + + it('should diagnose error on empty text', async () => { + const text = readJvTestAsset('valid-first-line.jv'); + + const testFile = readTestFile('test-empty.txt'); + const result = await parseAndExecuteExecutor(text, testFile); + + expect(R.isOk(result)).toEqual(false); + if (R.isErr(result)) { + expect(result.left.message).toEqual( + 'Line 1 does not exist in the text file, only 0 line(s) are present', + ); + } + }); + + it('should diagnose no error on duplicate line', async () => { + const text = readJvTestAsset('valid-duplicate-line.jv'); + + const testFile = readTestFile('test.txt'); + const result = await parseAndExecuteExecutor(text, testFile); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.TEXT_FILE); + expect(result.right.content).toEqual( + expect.arrayContaining(['Test File']), + ); + } + }); +}); diff --git a/libs/extensions/std/exec/src/text-line-deleter-executor.ts b/libs/extensions/std/exec/src/text-line-deleter-executor.ts new file mode 100644 index 00000000..c1fa2179 --- /dev/null +++ b/libs/extensions/std/exec/src/text-line-deleter-executor.ts @@ -0,0 +1,70 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import * as R from '@jvalue/jayvee-execution'; +import { + AbstractBlockExecutor, + type BlockExecutorClass, + type ExecutionContext, + TextFile, + implementsStatic, +} from '@jvalue/jayvee-execution'; +import { IOType } from '@jvalue/jayvee-language-server'; + +@implementsStatic<BlockExecutorClass>() +export class TextLineDeleterExecutor extends AbstractBlockExecutor< + IOType.TEXT_FILE, + IOType.TEXT_FILE +> { + public static readonly type = 'TextLineDeleter'; + + constructor() { + super(IOType.TEXT_FILE, IOType.TEXT_FILE); + } + + // eslint-disable-next-line @typescript-eslint/require-await + async doExecute( + file: TextFile, + context: ExecutionContext, + ): Promise<R.Result<TextFile>> { + const lines = context.getPropertyValue( + 'lines', + context.valueTypeProvider.createCollectionValueTypeOf( + context.valueTypeProvider.Primitives.Integer, + ), + ); + const numberOfLines = file.content.length; + + let lineIndex = 0; + for (const lineNumber of lines) { + if (lineNumber > numberOfLines) { + return R.err({ + message: `Line ${lineNumber} does not exist in the text file, only ${file.content.length} line(s) are present`, + diagnostic: { + node: context.getOrFailProperty('lines').value, + property: 'values', + index: lineIndex, + }, + }); + } + + ++lineIndex; + } + + const distinctLines = new Set(lines); + const sortedLines = [...distinctLines].sort((a, b) => a - b); + + context.logger.logDebug(`Deleting line(s) ${sortedLines.join(', ')}`); + + const reversedLines = sortedLines.reverse(); + const newContent = [...file.content]; + for (const lineToDelete of reversedLines) { + newContent.splice(lineToDelete - 1, 1); + } + + return R.ok( + new TextFile(file.name, file.extension, file.mimeType, newContent), + ); + } +} diff --git a/libs/extensions/std/exec/src/text-range-selector-executor.spec.ts b/libs/extensions/std/exec/src/text-range-selector-executor.spec.ts new file mode 100644 index 00000000..29a0eac4 --- /dev/null +++ b/libs/extensions/std/exec/src/text-range-selector-executor.spec.ts @@ -0,0 +1,113 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import path from 'node:path'; + +import * as R from '@jvalue/jayvee-execution'; +import { + createTextFileFromLocalFile, + getTestExecutionContext, +} from '@jvalue/jayvee-execution/test'; +import { + type BlockDefinition, + IOType, + type JayveeServices, + createJayveeServices, +} from '@jvalue/jayvee-language-server'; +import { + type ParseHelperOptions, + expectNoParserAndLexerErrors, + loadTestExtensions, + parseHelper, + readJvTestAssetHelper, +} from '@jvalue/jayvee-language-server/test'; +import { + type AstNode, + type AstNodeLocator, + type LangiumDocument, +} from 'langium'; +import { NodeFileSystem } from 'langium/node'; + +import { TextRangeSelectorExecutor } from './text-range-selector-executor'; + +describe('Validation of TextRangeSelectorExecutor', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + let locator: AstNodeLocator; + let services: JayveeServices; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../test/assets/text-range-selector-executor/', + ); + + function readTestFile(fileName: string): R.TextFile { + const absoluteFileName = path.resolve( + __dirname, + '../test/assets/text-range-selector-executor/', + fileName, + ); + return createTextFileFromLocalFile(absoluteFileName); + } + + async function parseAndExecuteExecutor( + input: string, + IOInput: R.TextFile, + ): Promise<R.Result<R.TextFile>> { + const document = await parse(input, { validation: true }); + expectNoParserAndLexerErrors(document); + + const block = locator.getAstNode<BlockDefinition>( + document.parseResult.value, + 'pipelines@0/blocks@1', + ) as BlockDefinition; + + return new TextRangeSelectorExecutor().doExecute( + IOInput, + getTestExecutionContext(locator, document, services, [block]), + ); + } + + beforeAll(async () => { + // Create language services + services = createJayveeServices(NodeFileSystem).Jayvee; + await loadTestExtensions(services, [ + path.resolve(__dirname, '../test/test-extension/TestBlockTypes.jv'), + ]); + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + it('should diagnose no error on valid file', async () => { + const text = readJvTestAsset('valid-range.jv'); + + const testFile = readTestFile('test.txt'); + const result = await parseAndExecuteExecutor(text, testFile); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.TEXT_FILE); + expect(result.right.content).toEqual( + expect.arrayContaining(['Multiline', 'Test ']), + ); + } + }); + + it('should diagnose no error on empty text file', async () => { + const text = readJvTestAsset('valid-range.jv'); + + const testFile = readTestFile('test-empty.txt'); + const result = await parseAndExecuteExecutor(text, testFile); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.TEXT_FILE); + expect(result.right.content).toHaveLength(0); + } + }); +}); diff --git a/libs/extensions/std/exec/src/text-range-selector-executor.ts b/libs/extensions/std/exec/src/text-range-selector-executor.ts new file mode 100644 index 00000000..da71adaa --- /dev/null +++ b/libs/extensions/std/exec/src/text-range-selector-executor.ts @@ -0,0 +1,55 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import * as R from '@jvalue/jayvee-execution'; +import { + AbstractBlockExecutor, + type BlockExecutorClass, + type ExecutionContext, + TextFile, + implementsStatic, +} from '@jvalue/jayvee-execution'; +import { IOType } from '@jvalue/jayvee-language-server'; + +@implementsStatic<BlockExecutorClass>() +export class TextRangeSelectorExecutor extends AbstractBlockExecutor< + IOType.TEXT_FILE, + IOType.TEXT_FILE +> { + public static readonly type = 'TextRangeSelector'; + + constructor() { + super(IOType.TEXT_FILE, IOType.TEXT_FILE); + } + + // eslint-disable-next-line @typescript-eslint/require-await + async doExecute( + file: TextFile, + context: ExecutionContext, + ): Promise<R.Result<TextFile>> { + const lineFrom = context.getPropertyValue( + 'lineFrom', + context.valueTypeProvider.Primitives.Integer, + ); + const lineTo = context.getPropertyValue( + 'lineTo', + context.valueTypeProvider.Primitives.Integer, + ); + + const numberOfLines = file.content.length; + + context.logger.logDebug( + `Selecting lines from ${lineFrom} to ${ + lineTo === Number.MAX_SAFE_INTEGER || lineTo >= numberOfLines + ? 'the end' + : `${lineTo}` + }`, + ); + const selectedLines = file.content.slice(lineFrom - 1, lineTo); + + return R.ok( + new TextFile(file.name, file.extension, file.mimeType, selectedLines), + ); + } +} diff --git a/libs/extensions/std/exec/src/util/backoff-strategy.spec.ts b/libs/extensions/std/exec/src/util/backoff-strategy.spec.ts new file mode 100644 index 00000000..7579cfb1 --- /dev/null +++ b/libs/extensions/std/exec/src/util/backoff-strategy.spec.ts @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type BackoffStrategy, + ExponentialBackoffStrategy, + LinearBackoffStrategy, +} from './backoff-strategy'; + +describe('BackoffStrategy', () => { + describe('ExponentialBackoffStrategy', () => { + describe('getBackoffMilliseconds', () => { + it('should calculate exponential backoff correctly with 5 retries', () => { + const backoffStrategy: BackoffStrategy = new ExponentialBackoffStrategy( + 2000, + ); + + expect(backoffStrategy.getBackoffMilliseconds(1)).toEqual(2000); + expect(backoffStrategy.getBackoffMilliseconds(2)).toEqual(4000); + expect(backoffStrategy.getBackoffMilliseconds(3)).toEqual(8000); + expect(backoffStrategy.getBackoffMilliseconds(4)).toEqual(16000); + expect(backoffStrategy.getBackoffMilliseconds(5)).toEqual(32000); + }); + }); + }); + + describe('LinearBackoffStrategy', () => { + describe('getNextBackoffMilliseconds', () => { + it('should calculate exponential backoff correctly with 5 retries', () => { + const backoffStrategy: BackoffStrategy = new LinearBackoffStrategy( + 2000, + ); + + expect(backoffStrategy.getBackoffMilliseconds(1)).toEqual(2000); + expect(backoffStrategy.getBackoffMilliseconds(2)).toEqual(2000); + expect(backoffStrategy.getBackoffMilliseconds(3)).toEqual(2000); + expect(backoffStrategy.getBackoffMilliseconds(4)).toEqual(2000); + expect(backoffStrategy.getBackoffMilliseconds(5)).toEqual(2000); + }); + }); + }); +}); diff --git a/libs/extensions/std/exec/src/util/backoff-strategy.ts b/libs/extensions/std/exec/src/util/backoff-strategy.ts new file mode 100644 index 00000000..0dfcb20d --- /dev/null +++ b/libs/extensions/std/exec/src/util/backoff-strategy.ts @@ -0,0 +1,52 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +export type BackoffStrategyHandle = 'exponential' | 'linear'; +export function isBackoffStrategyHandle( + v: unknown, +): v is BackoffStrategyHandle { + return v === 'exponential' || v === 'linear'; +} + +export function createBackoffStrategy( + handle: BackoffStrategyHandle, + backoffMilliseconds: number, +): BackoffStrategy { + if (handle === 'linear') { + return new LinearBackoffStrategy(backoffMilliseconds); + } + return new ExponentialBackoffStrategy(backoffMilliseconds); +} + +export interface BackoffStrategy { + /** + * Calculates the backoff interval in milliseconds. + * @param retry the number of the current retry, starts counting with 1 + */ + getBackoffMilliseconds(retry: number): number; +} + +/** + * Strategy for exponential backoffs. + * Uses seconds as unit for calculating the exponent. + */ +export class ExponentialBackoffStrategy implements BackoffStrategy { + constructor(private initialBackoffMilliseconds: number) {} + + getBackoffMilliseconds(retry: number): number { + const initialBackoffSeconds = this.initialBackoffMilliseconds / 1000; + return Math.pow(initialBackoffSeconds, retry) * 1000; + } +} + +/** + * Strategy for linear backoffs. + */ +export class LinearBackoffStrategy implements BackoffStrategy { + constructor(private backoffMilliseconds: number) {} + + getBackoffMilliseconds(): number { + return this.backoffMilliseconds; + } +} diff --git a/libs/extensions/std/exec/test/assets/archive-interpreter-executor/invalid-corrupt-zip.zip b/libs/extensions/std/exec/test/assets/archive-interpreter-executor/invalid-corrupt-zip.zip new file mode 100644 index 00000000..0168c1c1 --- /dev/null +++ b/libs/extensions/std/exec/test/assets/archive-interpreter-executor/invalid-corrupt-zip.zip @@ -0,0 +1 @@ +7UG5w4#K`^n<$=vo*[Qؤ~ Yg0{Qa! H'#xFg )+d\UקƔ^˧JC<f7o,"[Z&$hbJA%R4j%)ڈ;`ȾEۣWE/j \ No newline at end of file diff --git a/libs/extensions/std/exec/test/assets/archive-interpreter-executor/invalid-corrupt-zip.zip.license b/libs/extensions/std/exec/test/assets/archive-interpreter-executor/invalid-corrupt-zip.zip.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/std/exec/test/assets/archive-interpreter-executor/invalid-corrupt-zip.zip.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/std/exec/test/assets/archive-interpreter-executor/valid-7z-archive-interpreter.jv b/libs/extensions/std/exec/test/assets/archive-interpreter-executor/valid-7z-archive-interpreter.jv new file mode 100644 index 00000000..90854112 --- /dev/null +++ b/libs/extensions/std/exec/test/assets/archive-interpreter-executor/valid-7z-archive-interpreter.jv @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestBlock oftype ArchiveInterpreter { + archiveType: '7z'; + } + + block TestLoader oftype TestFileLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/std/exec/test/assets/archive-interpreter-executor/valid-7z.7z b/libs/extensions/std/exec/test/assets/archive-interpreter-executor/valid-7z.7z new file mode 100644 index 00000000..abf9b605 Binary files /dev/null and b/libs/extensions/std/exec/test/assets/archive-interpreter-executor/valid-7z.7z differ diff --git a/libs/extensions/std/exec/test/assets/archive-interpreter-executor/valid-7z.7z.license b/libs/extensions/std/exec/test/assets/archive-interpreter-executor/valid-7z.7z.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/std/exec/test/assets/archive-interpreter-executor/valid-7z.7z.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/std/exec/test/assets/archive-interpreter-executor/valid-gz-archive-interpreter.jv b/libs/extensions/std/exec/test/assets/archive-interpreter-executor/valid-gz-archive-interpreter.jv new file mode 100644 index 00000000..eb44f693 --- /dev/null +++ b/libs/extensions/std/exec/test/assets/archive-interpreter-executor/valid-gz-archive-interpreter.jv @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestBlock oftype ArchiveInterpreter { + archiveType: 'gz'; + } + + block TestLoader oftype TestFileLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/std/exec/test/assets/archive-interpreter-executor/valid-gz.gz b/libs/extensions/std/exec/test/assets/archive-interpreter-executor/valid-gz.gz new file mode 100644 index 00000000..87c5b997 Binary files /dev/null and b/libs/extensions/std/exec/test/assets/archive-interpreter-executor/valid-gz.gz differ diff --git a/libs/extensions/std/exec/test/assets/archive-interpreter-executor/valid-gz.gz.license b/libs/extensions/std/exec/test/assets/archive-interpreter-executor/valid-gz.gz.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/std/exec/test/assets/archive-interpreter-executor/valid-gz.gz.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/std/exec/test/assets/archive-interpreter-executor/valid-zip-archive-interpreter.jv b/libs/extensions/std/exec/test/assets/archive-interpreter-executor/valid-zip-archive-interpreter.jv new file mode 100644 index 00000000..b532faa4 --- /dev/null +++ b/libs/extensions/std/exec/test/assets/archive-interpreter-executor/valid-zip-archive-interpreter.jv @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestBlock oftype ArchiveInterpreter { + archiveType: 'zip'; + } + + block TestLoader oftype TestFileLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/std/exec/test/assets/archive-interpreter-executor/valid-zip.zip b/libs/extensions/std/exec/test/assets/archive-interpreter-executor/valid-zip.zip new file mode 100644 index 00000000..26f1dea6 Binary files /dev/null and b/libs/extensions/std/exec/test/assets/archive-interpreter-executor/valid-zip.zip differ diff --git a/libs/extensions/std/exec/test/assets/archive-interpreter-executor/valid-zip.zip.license b/libs/extensions/std/exec/test/assets/archive-interpreter-executor/valid-zip.zip.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/std/exec/test/assets/archive-interpreter-executor/valid-zip.zip.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/std/exec/test/assets/file-picker-executor/dot-valid-file-picker.jv b/libs/extensions/std/exec/test/assets/file-picker-executor/dot-valid-file-picker.jv new file mode 100644 index 00000000..ccb5c334 --- /dev/null +++ b/libs/extensions/std/exec/test/assets/file-picker-executor/dot-valid-file-picker.jv @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestBlock oftype FilePicker { + path: './test.txt'; + } + + block TestLoader oftype TestFileLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/std/exec/test/assets/file-picker-executor/test.txt b/libs/extensions/std/exec/test/assets/file-picker-executor/test.txt new file mode 100644 index 00000000..4fff881e --- /dev/null +++ b/libs/extensions/std/exec/test/assets/file-picker-executor/test.txt @@ -0,0 +1 @@ +Test File diff --git a/libs/extensions/std/exec/test/assets/file-picker-executor/test.txt.license b/libs/extensions/std/exec/test/assets/file-picker-executor/test.txt.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/std/exec/test/assets/file-picker-executor/test.txt.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/std/exec/test/assets/file-picker-executor/valid-file-picker.jv b/libs/extensions/std/exec/test/assets/file-picker-executor/valid-file-picker.jv new file mode 100644 index 00000000..169d79c4 --- /dev/null +++ b/libs/extensions/std/exec/test/assets/file-picker-executor/valid-file-picker.jv @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestBlock oftype FilePicker { + path: '/test.txt'; + } + + block TestLoader oftype TestFileLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/std/exec/test/assets/gtfs-rt-interpreter-executor/gtfs/invalid-gtfs b/libs/extensions/std/exec/test/assets/gtfs-rt-interpreter-executor/gtfs/invalid-gtfs new file mode 100644 index 00000000..5a9d444b --- /dev/null +++ b/libs/extensions/std/exec/test/assets/gtfs-rt-interpreter-executor/gtfs/invalid-gtfs @@ -0,0 +1 @@ +This is not a gtfs file diff --git a/libs/extensions/std/exec/test/assets/gtfs-rt-interpreter-executor/gtfs/invalid-gtfs.license b/libs/extensions/std/exec/test/assets/gtfs-rt-interpreter-executor/gtfs/invalid-gtfs.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/std/exec/test/assets/gtfs-rt-interpreter-executor/gtfs/invalid-gtfs.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/std/exec/test/assets/gtfs-rt-interpreter-executor/gtfs/valid-alerts.json b/libs/extensions/std/exec/test/assets/gtfs-rt-interpreter-executor/gtfs/valid-alerts.json new file mode 100644 index 00000000..aebc307d Binary files /dev/null and b/libs/extensions/std/exec/test/assets/gtfs-rt-interpreter-executor/gtfs/valid-alerts.json differ diff --git a/libs/extensions/std/exec/test/assets/gtfs-rt-interpreter-executor/gtfs/valid-alerts.json.license b/libs/extensions/std/exec/test/assets/gtfs-rt-interpreter-executor/gtfs/valid-alerts.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/std/exec/test/assets/gtfs-rt-interpreter-executor/gtfs/valid-alerts.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/std/exec/test/assets/gtfs-rt-interpreter-executor/gtfs/valid-trip-update b/libs/extensions/std/exec/test/assets/gtfs-rt-interpreter-executor/gtfs/valid-trip-update new file mode 100644 index 00000000..14823623 Binary files /dev/null and b/libs/extensions/std/exec/test/assets/gtfs-rt-interpreter-executor/gtfs/valid-trip-update differ diff --git a/libs/extensions/std/exec/test/assets/gtfs-rt-interpreter-executor/gtfs/valid-trip-update.license b/libs/extensions/std/exec/test/assets/gtfs-rt-interpreter-executor/gtfs/valid-trip-update.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/std/exec/test/assets/gtfs-rt-interpreter-executor/gtfs/valid-trip-update.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/std/exec/test/assets/gtfs-rt-interpreter-executor/gtfs/valid-vehicle b/libs/extensions/std/exec/test/assets/gtfs-rt-interpreter-executor/gtfs/valid-vehicle new file mode 100644 index 00000000..944ad0dc Binary files /dev/null and b/libs/extensions/std/exec/test/assets/gtfs-rt-interpreter-executor/gtfs/valid-vehicle differ diff --git a/libs/extensions/std/exec/test/assets/gtfs-rt-interpreter-executor/gtfs/valid-vehicle.license b/libs/extensions/std/exec/test/assets/gtfs-rt-interpreter-executor/gtfs/valid-vehicle.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/std/exec/test/assets/gtfs-rt-interpreter-executor/gtfs/valid-vehicle.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/std/exec/test/assets/gtfs-rt-interpreter-executor/invalid-entity-parameter.jv b/libs/extensions/std/exec/test/assets/gtfs-rt-interpreter-executor/invalid-entity-parameter.jv new file mode 100644 index 00000000..1b8d7472 --- /dev/null +++ b/libs/extensions/std/exec/test/assets/gtfs-rt-interpreter-executor/invalid-entity-parameter.jv @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestBlock oftype GtfsRTInterpreter { + entity: 'invalid'; + } + + block TestLoader oftype TestSheetLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/std/exec/test/assets/gtfs-rt-interpreter-executor/valid-alerts-gtfs-interpreter.jv b/libs/extensions/std/exec/test/assets/gtfs-rt-interpreter-executor/valid-alerts-gtfs-interpreter.jv new file mode 100644 index 00000000..10b666a3 --- /dev/null +++ b/libs/extensions/std/exec/test/assets/gtfs-rt-interpreter-executor/valid-alerts-gtfs-interpreter.jv @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestBlock oftype GtfsRTInterpreter { + entity: 'alert'; + } + + block TestLoader oftype TestSheetLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/std/exec/test/assets/gtfs-rt-interpreter-executor/valid-trip-update-gtfs-interpreter.jv b/libs/extensions/std/exec/test/assets/gtfs-rt-interpreter-executor/valid-trip-update-gtfs-interpreter.jv new file mode 100644 index 00000000..e2decf14 --- /dev/null +++ b/libs/extensions/std/exec/test/assets/gtfs-rt-interpreter-executor/valid-trip-update-gtfs-interpreter.jv @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestBlock oftype GtfsRTInterpreter { + entity: 'trip_update'; + } + + block TestLoader oftype TestSheetLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/std/exec/test/assets/gtfs-rt-interpreter-executor/valid-vehicle-gtfs-interpreter.jv b/libs/extensions/std/exec/test/assets/gtfs-rt-interpreter-executor/valid-vehicle-gtfs-interpreter.jv new file mode 100644 index 00000000..0f9be12a --- /dev/null +++ b/libs/extensions/std/exec/test/assets/gtfs-rt-interpreter-executor/valid-vehicle-gtfs-interpreter.jv @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestBlock oftype GtfsRTInterpreter { + entity: 'vehicle'; + } + + block TestLoader oftype TestSheetLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/std/exec/test/assets/http-extractor-executor/test.txt b/libs/extensions/std/exec/test/assets/http-extractor-executor/test.txt new file mode 100644 index 00000000..4fff881e --- /dev/null +++ b/libs/extensions/std/exec/test/assets/http-extractor-executor/test.txt @@ -0,0 +1 @@ +Test File diff --git a/libs/extensions/std/exec/test/assets/http-extractor-executor/test.txt.license b/libs/extensions/std/exec/test/assets/http-extractor-executor/test.txt.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/std/exec/test/assets/http-extractor-executor/test.txt.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/std/exec/test/assets/http-extractor-executor/valid-http.jv b/libs/extensions/std/exec/test/assets/http-extractor-executor/valid-http.jv new file mode 100644 index 00000000..e932e787 --- /dev/null +++ b/libs/extensions/std/exec/test/assets/http-extractor-executor/valid-http.jv @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestBlock oftype HttpExtractor { + url: 'http://localhost/test.txt'; + } + + block TestLoader oftype TestSheetLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/std/exec/test/assets/http-extractor-executor/valid-https.jv b/libs/extensions/std/exec/test/assets/http-extractor-executor/valid-https.jv new file mode 100644 index 00000000..cdc9ef43 --- /dev/null +++ b/libs/extensions/std/exec/test/assets/http-extractor-executor/valid-https.jv @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestBlock oftype HttpExtractor { + url: 'https://localhost/test.txt'; + } + + block TestLoader oftype TestSheetLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/std/exec/test/assets/http-extractor-executor/valid-ignore-redirects.jv b/libs/extensions/std/exec/test/assets/http-extractor-executor/valid-ignore-redirects.jv new file mode 100644 index 00000000..9b3793ba --- /dev/null +++ b/libs/extensions/std/exec/test/assets/http-extractor-executor/valid-ignore-redirects.jv @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestBlock oftype HttpExtractor { + url: 'https://localhost/test.txt'; + followRedirects: false; + } + + block TestLoader oftype TestSheetLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/std/exec/test/assets/http-extractor-executor/valid-one-retry.jv b/libs/extensions/std/exec/test/assets/http-extractor-executor/valid-one-retry.jv new file mode 100644 index 00000000..6760e797 --- /dev/null +++ b/libs/extensions/std/exec/test/assets/http-extractor-executor/valid-one-retry.jv @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestBlock oftype HttpExtractor { + url: 'https://localhost/test.txt'; + retries: 1; + } + + block TestLoader oftype TestSheetLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/std/exec/test/assets/local-file-extractor-executor/invalid-file-not-found.jv b/libs/extensions/std/exec/test/assets/local-file-extractor-executor/invalid-file-not-found.jv new file mode 100644 index 00000000..6030d55a --- /dev/null +++ b/libs/extensions/std/exec/test/assets/local-file-extractor-executor/invalid-file-not-found.jv @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestBlock oftype LocalFileExtractor { + filePath: './does-not-exist.csv'; + } + + block TestLoader oftype TestSheetLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/std/exec/test/assets/local-file-extractor-executor/invalid-path-traversal-at-start.jv b/libs/extensions/std/exec/test/assets/local-file-extractor-executor/invalid-path-traversal-at-start.jv new file mode 100644 index 00000000..464751f1 --- /dev/null +++ b/libs/extensions/std/exec/test/assets/local-file-extractor-executor/invalid-path-traversal-at-start.jv @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestBlock oftype LocalFileExtractor { + filePath: '../non-existent-file.csv'; + } + + block TestLoader oftype TestSheetLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} \ No newline at end of file diff --git a/libs/extensions/std/exec/test/assets/local-file-extractor-executor/invalid-path-traversal-in-path.jv b/libs/extensions/std/exec/test/assets/local-file-extractor-executor/invalid-path-traversal-in-path.jv new file mode 100644 index 00000000..23a0aeed --- /dev/null +++ b/libs/extensions/std/exec/test/assets/local-file-extractor-executor/invalid-path-traversal-in-path.jv @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestBlock oftype LocalFileExtractor { + filePath: './../non-existent-file.csv'; + } + + block TestLoader oftype TestSheetLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} \ No newline at end of file diff --git a/libs/extensions/std/exec/test/assets/local-file-extractor-executor/local-file-test.csv b/libs/extensions/std/exec/test/assets/local-file-extractor-executor/local-file-test.csv new file mode 100644 index 00000000..e336857b --- /dev/null +++ b/libs/extensions/std/exec/test/assets/local-file-extractor-executor/local-file-test.csv @@ -0,0 +1,9 @@ +HeaderExample1,HeaderExample2,HeaderExample3,HeaderExample4 +Example1,Example2,Example3,Example4 +Example1,Example2,Example3,Example4 +Example1,Example2,Example3,Example4 +Example1,Example2,Example3,Example4 +Example1,Example2,Example3,Example4 +Example1,Example2,Example3,Example4 +Example1,Example2,Example3,Example4 +Example1,Example2,Example3,Example4 \ No newline at end of file diff --git a/libs/extensions/std/exec/test/assets/local-file-extractor-executor/local-file-test.csv.license b/libs/extensions/std/exec/test/assets/local-file-extractor-executor/local-file-test.csv.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/libs/extensions/std/exec/test/assets/local-file-extractor-executor/local-file-test.csv.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/libs/extensions/std/exec/test/assets/local-file-extractor-executor/valid-local-file.jv b/libs/extensions/std/exec/test/assets/local-file-extractor-executor/valid-local-file.jv new file mode 100644 index 00000000..6ef89cfe --- /dev/null +++ b/libs/extensions/std/exec/test/assets/local-file-extractor-executor/valid-local-file.jv @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestBlock oftype LocalFileExtractor { + filePath: './libs/extensions/std/exec/test/assets/local-file-extractor-executor/local-file-test.csv'; + } + + block TestLoader oftype TestSheetLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} \ No newline at end of file diff --git a/libs/extensions/std/exec/test/assets/text-file-interpreter-executor/gtfs-vehicle b/libs/extensions/std/exec/test/assets/text-file-interpreter-executor/gtfs-vehicle new file mode 100644 index 00000000..944ad0dc Binary files /dev/null and b/libs/extensions/std/exec/test/assets/text-file-interpreter-executor/gtfs-vehicle differ diff --git a/libs/extensions/std/exec/test/assets/text-file-interpreter-executor/gtfs-vehicle.license b/libs/extensions/std/exec/test/assets/text-file-interpreter-executor/gtfs-vehicle.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/std/exec/test/assets/text-file-interpreter-executor/gtfs-vehicle.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/std/exec/test/assets/text-file-interpreter-executor/test.txt b/libs/extensions/std/exec/test/assets/text-file-interpreter-executor/test.txt new file mode 100644 index 00000000..b118aec1 --- /dev/null +++ b/libs/extensions/std/exec/test/assets/text-file-interpreter-executor/test.txt @@ -0,0 +1,2 @@ +Multiline +Test File diff --git a/libs/extensions/std/exec/test/assets/text-file-interpreter-executor/test.txt.license b/libs/extensions/std/exec/test/assets/text-file-interpreter-executor/test.txt.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/std/exec/test/assets/text-file-interpreter-executor/test.txt.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/std/exec/test/assets/text-file-interpreter-executor/valid-custom-line-break.jv b/libs/extensions/std/exec/test/assets/text-file-interpreter-executor/valid-custom-line-break.jv new file mode 100644 index 00000000..cb5aa5ce --- /dev/null +++ b/libs/extensions/std/exec/test/assets/text-file-interpreter-executor/valid-custom-line-break.jv @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestBlock oftype TextFileInterpreter { + lineBreak: / /; + } + + block TestLoader oftype TestTextFileLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/std/exec/test/assets/text-file-interpreter-executor/valid-default-file-interpreter.jv b/libs/extensions/std/exec/test/assets/text-file-interpreter-executor/valid-default-file-interpreter.jv new file mode 100644 index 00000000..864a10d6 --- /dev/null +++ b/libs/extensions/std/exec/test/assets/text-file-interpreter-executor/valid-default-file-interpreter.jv @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestBlock oftype TextFileInterpreter { + } + + block TestLoader oftype TestTextFileLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/std/exec/test/assets/text-line-deleter-executor/test-empty.txt b/libs/extensions/std/exec/test/assets/text-line-deleter-executor/test-empty.txt new file mode 100644 index 00000000..e69de29b diff --git a/libs/extensions/std/exec/test/assets/text-line-deleter-executor/test-empty.txt.license b/libs/extensions/std/exec/test/assets/text-line-deleter-executor/test-empty.txt.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/std/exec/test/assets/text-line-deleter-executor/test-empty.txt.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/std/exec/test/assets/text-line-deleter-executor/test.txt b/libs/extensions/std/exec/test/assets/text-line-deleter-executor/test.txt new file mode 100644 index 00000000..ab23659f --- /dev/null +++ b/libs/extensions/std/exec/test/assets/text-line-deleter-executor/test.txt @@ -0,0 +1,2 @@ +Multiline +Test File diff --git a/libs/extensions/std/exec/test/assets/text-line-deleter-executor/test.txt.license b/libs/extensions/std/exec/test/assets/text-line-deleter-executor/test.txt.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/std/exec/test/assets/text-line-deleter-executor/test.txt.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/std/exec/test/assets/text-line-deleter-executor/valid-duplicate-line.jv b/libs/extensions/std/exec/test/assets/text-line-deleter-executor/valid-duplicate-line.jv new file mode 100644 index 00000000..bee37112 --- /dev/null +++ b/libs/extensions/std/exec/test/assets/text-line-deleter-executor/valid-duplicate-line.jv @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestBlock oftype TextLineDeleter { + lines: [1,1]; + } + + block TestLoader oftype TestTextFileLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/std/exec/test/assets/text-line-deleter-executor/valid-first-line.jv b/libs/extensions/std/exec/test/assets/text-line-deleter-executor/valid-first-line.jv new file mode 100644 index 00000000..c0d367ad --- /dev/null +++ b/libs/extensions/std/exec/test/assets/text-line-deleter-executor/valid-first-line.jv @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestBlock oftype TextLineDeleter { + lines: [1]; + } + + block TestLoader oftype TestTextFileLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/std/exec/test/assets/text-line-deleter-executor/valid-no-line.jv b/libs/extensions/std/exec/test/assets/text-line-deleter-executor/valid-no-line.jv new file mode 100644 index 00000000..6f589315 --- /dev/null +++ b/libs/extensions/std/exec/test/assets/text-line-deleter-executor/valid-no-line.jv @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestBlock oftype TextLineDeleter { + lines: []; + } + + block TestLoader oftype TestTextFileLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/std/exec/test/assets/text-range-selector-executor/test-empty.txt b/libs/extensions/std/exec/test/assets/text-range-selector-executor/test-empty.txt new file mode 100644 index 00000000..e69de29b diff --git a/libs/extensions/std/exec/test/assets/text-range-selector-executor/test-empty.txt.license b/libs/extensions/std/exec/test/assets/text-range-selector-executor/test-empty.txt.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/std/exec/test/assets/text-range-selector-executor/test-empty.txt.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/std/exec/test/assets/text-range-selector-executor/test.txt b/libs/extensions/std/exec/test/assets/text-range-selector-executor/test.txt new file mode 100644 index 00000000..5f660012 --- /dev/null +++ b/libs/extensions/std/exec/test/assets/text-range-selector-executor/test.txt @@ -0,0 +1,3 @@ +Multiline +Test +File diff --git a/libs/extensions/std/exec/test/assets/text-range-selector-executor/test.txt.license b/libs/extensions/std/exec/test/assets/text-range-selector-executor/test.txt.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/std/exec/test/assets/text-range-selector-executor/test.txt.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/std/exec/test/assets/text-range-selector-executor/valid-range.jv b/libs/extensions/std/exec/test/assets/text-range-selector-executor/valid-range.jv new file mode 100644 index 00000000..13059cce --- /dev/null +++ b/libs/extensions/std/exec/test/assets/text-range-selector-executor/valid-range.jv @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestBlock oftype TextRangeSelector { + lineFrom: 1; + lineTo: 2; + } + + block TestLoader oftype TestTextFileLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/std/exec/test/index.ts b/libs/extensions/std/exec/test/index.ts new file mode 100644 index 00000000..460aca6a --- /dev/null +++ b/libs/extensions/std/exec/test/index.ts @@ -0,0 +1,5 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +export * from './mocks'; diff --git a/libs/extensions/std/exec/test/mocks/http-extractor-executor-mock.ts b/libs/extensions/std/exec/test/mocks/http-extractor-executor-mock.ts new file mode 100644 index 00000000..8651d7d2 --- /dev/null +++ b/libs/extensions/std/exec/test/mocks/http-extractor-executor-mock.ts @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type BlockExecutorMock } from '@jvalue/jayvee-execution/test'; +import * as nock from 'nock'; + +export class HttpExtractorExecutorMock implements BlockExecutorMock { + private _nockScopes: nock.Scope[] = []; + + get nockScopes(): nock.Scope[] { + return this._nockScopes; + } + + setup(registerMocks: () => nock.Scope[]) { + // setup nock + if (!nock.isActive()) { + nock.activate(); + } + this._nockScopes = registerMocks(); + } + restore() { + // cleanup nock interceptors and scopes + nock.restore(); + this._nockScopes = []; + } +} diff --git a/libs/extensions/std/exec/test/mocks/index.ts b/libs/extensions/std/exec/test/mocks/index.ts new file mode 100644 index 00000000..e8dccc9e --- /dev/null +++ b/libs/extensions/std/exec/test/mocks/index.ts @@ -0,0 +1,5 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +export * from './http-extractor-executor-mock'; diff --git a/libs/extensions/std/exec/test/test-extension/TestBlockTypes.jv b/libs/extensions/std/exec/test/test-extension/TestBlockTypes.jv new file mode 100644 index 00000000..3abfdb4e --- /dev/null +++ b/libs/extensions/std/exec/test/test-extension/TestBlockTypes.jv @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +builtin blocktype TestFileExtractor { + input inPort oftype None; + output outPort oftype File; +} + +builtin blocktype TestFileLoader { + input inPort oftype File; + output outPort oftype None; +} + +builtin blocktype TestSheetLoader { + input inPort oftype Sheet; + output outPort oftype None; +} + +builtin blocktype TestTextFileLoader { + input inPort oftype TextFile; + output outPort oftype None; +} diff --git a/libs/extensions/std/exec/tsconfig.json b/libs/extensions/std/exec/tsconfig.json new file mode 100644 index 00000000..d2cccb9f --- /dev/null +++ b/libs/extensions/std/exec/tsconfig.json @@ -0,0 +1,21 @@ +{ + "extends": "../../../../tsconfig.base.json", + "compilerOptions": { + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/libs/extensions/std/exec/tsconfig.json.license b/libs/extensions/std/exec/tsconfig.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/std/exec/tsconfig.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/std/exec/tsconfig.lib.json b/libs/extensions/std/exec/tsconfig.lib.json new file mode 100644 index 00000000..6e6931cb --- /dev/null +++ b/libs/extensions/std/exec/tsconfig.lib.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../../dist/out-tsc", + "declaration": true, + "types": ["node"] + }, + "include": ["src/**/*.ts"], + "exclude": ["vite.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts", "test/**/*.ts"] +} diff --git a/libs/extensions/std/exec/tsconfig.lib.json.license b/libs/extensions/std/exec/tsconfig.lib.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/std/exec/tsconfig.lib.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/std/exec/tsconfig.spec.json b/libs/extensions/std/exec/tsconfig.spec.json new file mode 100644 index 00000000..1ddf3dc6 --- /dev/null +++ b/libs/extensions/std/exec/tsconfig.spec.json @@ -0,0 +1,27 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../../dist/out-tsc", + "types": [ + "vitest/globals", + "vitest/importMeta", + "vite/client", + "node", + "vitest" + ] + }, + "include": [ + "vite.config.ts", + "vitest.config.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.test.tsx", + "src/**/*.spec.tsx", + "src/**/*.test.js", + "src/**/*.spec.js", + "src/**/*.test.jsx", + "src/**/*.spec.jsx", + "src/**/*.d.ts", + "test/**/*.ts" + ] +} diff --git a/libs/extensions/std/exec/tsconfig.spec.json.license b/libs/extensions/std/exec/tsconfig.spec.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/std/exec/tsconfig.spec.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/std/exec/vite.config.ts b/libs/extensions/std/exec/vite.config.ts new file mode 100644 index 00000000..e78834c8 --- /dev/null +++ b/libs/extensions/std/exec/vite.config.ts @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// / <reference types='vitest' /> +import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + root: __dirname, + cacheDir: '../../../../node_modules/.vite/libs/extensions/std/exec', + + plugins: [nxViteTsPaths()], + + test: { + globals: true, + cacheDir: '../../../../node_modules/.vitest', + environment: 'jsdom', + include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], + + reporters: ['default'], + coverage: { + reportsDirectory: '../../../../coverage/libs/extensions/std/exec', + provider: 'v8', + }, + }, +}); diff --git a/libs/extensions/tabular/exec/.babelrc b/libs/extensions/tabular/exec/.babelrc new file mode 100644 index 00000000..fd4cbcde --- /dev/null +++ b/libs/extensions/tabular/exec/.babelrc @@ -0,0 +1,10 @@ +{ + "presets": [ + [ + "@nx/js/babel", + { + "useBuiltIns": "usage" + } + ] + ] +} diff --git a/libs/extensions/tabular/exec/.babelrc.license b/libs/extensions/tabular/exec/.babelrc.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/tabular/exec/.babelrc.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/tabular/exec/.eslintrc.json b/libs/extensions/tabular/exec/.eslintrc.json new file mode 100644 index 00000000..67b3411f --- /dev/null +++ b/libs/extensions/tabular/exec/.eslintrc.json @@ -0,0 +1,21 @@ +{ + "extends": ["../../../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "parserOptions": { + "project": ["libs/extensions/tabular/exec/tsconfig.lib.json", "libs/extensions/tabular/exec/tsconfig.spec.json"] + }, + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/libs/extensions/tabular/exec/.eslintrc.json.license b/libs/extensions/tabular/exec/.eslintrc.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/tabular/exec/.eslintrc.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/tabular/exec/README.md b/libs/extensions/tabular/exec/README.md new file mode 100644 index 00000000..c410786e --- /dev/null +++ b/libs/extensions/tabular/exec/README.md @@ -0,0 +1,17 @@ +<!-- +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only +--> + +# extensions-tabular-exec + +This library was generated with [Nx](https://nx.dev). + +## Building + +Run `nx build extensions-tabular-exec` to build the library. + +## Running unit tests + +Run `nx test extensions-tabular-exec` to execute the unit tests via [vitest](https://vitest.dev). diff --git a/libs/extensions/tabular/exec/package.json b/libs/extensions/tabular/exec/package.json new file mode 100644 index 00000000..cc24f141 --- /dev/null +++ b/libs/extensions/tabular/exec/package.json @@ -0,0 +1,3 @@ +{ + "name": "@jvalue/jayvee-extensions/tabular/exec" +} diff --git a/libs/extensions/tabular/exec/package.json.license b/libs/extensions/tabular/exec/package.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/tabular/exec/package.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/tabular/exec/project.json b/libs/extensions/tabular/exec/project.json new file mode 100644 index 00000000..e58d2ba9 --- /dev/null +++ b/libs/extensions/tabular/exec/project.json @@ -0,0 +1,22 @@ +{ + "name": "extensions-tabular-exec", + "$schema": "../../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/extensions/tabular/exec/src", + "projectType": "library", + "targets": { + "build": {}, + "lint": { + "executor": "@nx/eslint:lint", + "outputs": ["{options.outputFile}"] + }, + "test": { + "executor": "@nx/vite:test", + "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], + "options": { + "configFile": "{projectRoot}/vite.config.ts", + "passWithNoTests": false + } + } + }, + "tags": [] +} diff --git a/libs/extensions/tabular/exec/project.json.license b/libs/extensions/tabular/exec/project.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/tabular/exec/project.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/tabular/exec/src/extension.ts b/libs/extensions/tabular/exec/src/extension.ts new file mode 100644 index 00000000..c0280bb7 --- /dev/null +++ b/libs/extensions/tabular/exec/src/extension.ts @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type BlockExecutorClass, + JayveeExecExtension, +} from '@jvalue/jayvee-execution'; + +import { CellRangeSelectorExecutor } from './lib/cell-range-selector-executor'; +import { CellWriterExecutor } from './lib/cell-writer-executor'; +import { ColumnDeleterExecutor } from './lib/column-deleter-executor'; +import { CSVInterpreterExecutor } from './lib/csv-interpreter-executor'; +import { FileToTableInterpreterExecutor } from './lib/file-to-table-interpreter-executor'; +import { RowDeleterExecutor } from './lib/row-deleter-executor'; +import { SheetPickerExecutor } from './lib/sheet-picker-executor'; +import { + PolarsTableInterpreterExecutor, + TsTableInterpreterExecutor, +} from './lib/table-interpreter-executor'; +import { + PolarsTableTransformerExecutor, + TsTableTransformerExecutor, +} from './lib/table-transformer-executor'; +import { XLSXInterpreterExecutor } from './lib/xlsx-interpreter-executor'; + +export class TabularExecExtension extends JayveeExecExtension { + getBlockExecutors(): BlockExecutorClass[] { + return [ + CellWriterExecutor, + ColumnDeleterExecutor, + RowDeleterExecutor, + CellRangeSelectorExecutor, + PolarsTableInterpreterExecutor, + TsTableInterpreterExecutor, + CSVInterpreterExecutor, + PolarsTableTransformerExecutor, + TsTableTransformerExecutor, + XLSXInterpreterExecutor, + SheetPickerExecutor, + FileToTableInterpreterExecutor, + ]; + } +} diff --git a/libs/extensions/tabular/exec/src/index.ts b/libs/extensions/tabular/exec/src/index.ts new file mode 100644 index 00000000..88d3a004 --- /dev/null +++ b/libs/extensions/tabular/exec/src/index.ts @@ -0,0 +1,5 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +export * from './extension'; diff --git a/libs/extensions/tabular/exec/src/lib/cell-range-selector-executor.spec.ts b/libs/extensions/tabular/exec/src/lib/cell-range-selector-executor.spec.ts new file mode 100644 index 00000000..1f03f6fc --- /dev/null +++ b/libs/extensions/tabular/exec/src/lib/cell-range-selector-executor.spec.ts @@ -0,0 +1,182 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import path from 'node:path'; + +import * as R from '@jvalue/jayvee-execution'; +import { getTestExecutionContext } from '@jvalue/jayvee-execution/test'; +import { + type BlockDefinition, + IOType, + type JayveeServices, + createJayveeServices, +} from '@jvalue/jayvee-language-server'; +import { + type ParseHelperOptions, + expectNoParserAndLexerErrors, + loadTestExtensions, + parseHelper, + readJvTestAssetHelper, +} from '@jvalue/jayvee-language-server/test'; +import { + type AstNode, + type AstNodeLocator, + type LangiumDocument, +} from 'langium'; +import { NodeFileSystem } from 'langium/node'; + +import { createWorkbookFromLocalExcelFile } from '../../test/util'; + +import { CellRangeSelectorExecutor } from './cell-range-selector-executor'; + +describe('Validation of CellRangeSelectorExecutor', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + let locator: AstNodeLocator; + let services: JayveeServices; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../test/assets/cell-range-selector-executor/', + ); + + async function readTestWorkbook(fileName: string): Promise<R.Workbook> { + const absoluteFileName = path.resolve( + __dirname, + '../../test/assets/cell-range-selector-executor/', + fileName, + ); + return await createWorkbookFromLocalExcelFile(absoluteFileName); + } + + async function parseAndExecuteExecutor( + input: string, + IOInput: R.Sheet, + ): Promise<R.Result<R.Sheet>> { + const document = await parse(input, { validation: true }); + expectNoParserAndLexerErrors(document); + + const block = locator.getAstNode<BlockDefinition>( + document.parseResult.value, + 'pipelines@0/blocks@1', + ) as BlockDefinition; + + return new CellRangeSelectorExecutor().doExecute( + IOInput, + getTestExecutionContext(locator, document, services, [block]), + ); + } + + beforeAll(async () => { + // Create language services + services = createJayveeServices(NodeFileSystem).Jayvee; + await loadTestExtensions(services, [ + path.resolve(__dirname, '../../test/test-extension/TestBlockTypes.jv'), + ]); + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + it('should diagnose no error on valid selector', async () => { + const text = readJvTestAsset('valid-A1-C.jv'); + + const testWorkbook = await readTestWorkbook('test-A1-C16.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.SHEET); + expect(result.right.getNumberOfColumns()).toEqual(3); + expect(result.right.getNumberOfRows()).toEqual(16); + expect(result.right.getHeaderRow()).toEqual(['0', 'Test', 'true']); + expect(result.right.getData()).toEqual( + expect.arrayContaining([ + ['0', 'Test', 'true'], + ['1', 'Test', 'false'], + ['15', 'Test', 'true'], + ]), + ); + } + }); + + it('should diagnose no error on empty column', async () => { + const text = readJvTestAsset('valid-A1-C.jv'); + + const testWorkbook = await readTestWorkbook('test-B1-C2.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.SHEET); + expect(result.right.getNumberOfColumns()).toEqual(3); + expect(result.right.getNumberOfRows()).toEqual(2); + expect(result.right.getHeaderRow()).toEqual(['', 'Test', 'true']); + expect(result.right.getData()).toEqual([ + ['', 'Test', 'true'], + ['', 'Test', 'false'], + ]); + } + }); + + it('should diagnose error on selector out of bounds', async () => { + const text = readJvTestAsset('valid-A1-E4.jv'); + + const testWorkbook = await readTestWorkbook('test-A1-C16.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isOk(result)).toEqual(false); + if (R.isErr(result)) { + expect(result.left.message).toEqual( + 'The specified cell range does not fit the sheet', + ); + } + }); + + it('should diagnose error on selector on empty sheet', async () => { + const text = readJvTestAsset('valid-A1-C.jv'); + + const testWorkbook = await readTestWorkbook('test-empty.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isOk(result)).toEqual(false); + if (R.isErr(result)) { + expect(result.left.message).toEqual( + 'The specified cell range does not fit the sheet', + ); + } + }); + + it('should diagnose error on single column selector on empty sheet', async () => { + const text = readJvTestAsset('valid-A1-A4.jv'); + + const testWorkbook = await readTestWorkbook('test-empty.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isOk(result)).toEqual(false); + if (R.isErr(result)) { + expect(result.left.message).toEqual( + 'The specified cell range does not fit the sheet', + ); + } + }); +}); diff --git a/libs/extensions/tabular/exec/src/lib/cell-range-selector-executor.ts b/libs/extensions/tabular/exec/src/lib/cell-range-selector-executor.ts new file mode 100644 index 00000000..d0d075fa --- /dev/null +++ b/libs/extensions/tabular/exec/src/lib/cell-range-selector-executor.ts @@ -0,0 +1,55 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import * as R from '@jvalue/jayvee-execution'; +import { + AbstractBlockExecutor, + type BlockExecutorClass, + type ExecutionContext, + type Sheet, + implementsStatic, +} from '@jvalue/jayvee-execution'; +import { IOType } from '@jvalue/jayvee-language-server'; + +@implementsStatic<BlockExecutorClass>() +export class CellRangeSelectorExecutor extends AbstractBlockExecutor< + IOType.SHEET, + IOType.SHEET +> { + public static readonly type = 'CellRangeSelector'; + + constructor() { + super(IOType.SHEET, IOType.SHEET); + } + + // eslint-disable-next-line @typescript-eslint/require-await + async doExecute( + inputSheet: Sheet, + context: ExecutionContext, + ): Promise<R.Result<Sheet>> { + const relativeRange = context.getPropertyValue( + 'select', + context.valueTypeProvider.Primitives.CellRange, + ); + const relativeRangeWrapper = + context.wrapperFactories.CellRange.wrap(relativeRange); + + const absoluteRange = + inputSheet.resolveRelativeIndexes(relativeRangeWrapper); + + if (!inputSheet.isInBounds(absoluteRange)) { + return R.err({ + message: 'The specified cell range does not fit the sheet', + diagnostic: { node: absoluteRange.astNode }, + }); + } + + context.logger.logDebug(`Selecting cell range ${absoluteRange.toString()}`); + + const resultingSheet = inputSheet.clone(); + resultingSheet.selectRange(absoluteRange); + + return R.ok(resultingSheet); + } +} diff --git a/libs/extensions/tabular/exec/src/lib/cell-writer-executor.spec.ts b/libs/extensions/tabular/exec/src/lib/cell-writer-executor.spec.ts new file mode 100644 index 00000000..7f2c77a4 --- /dev/null +++ b/libs/extensions/tabular/exec/src/lib/cell-writer-executor.spec.ts @@ -0,0 +1,151 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import path from 'node:path'; + +import * as R from '@jvalue/jayvee-execution'; +import { getTestExecutionContext } from '@jvalue/jayvee-execution/test'; +import { + type BlockDefinition, + IOType, + type JayveeServices, + createJayveeServices, +} from '@jvalue/jayvee-language-server'; +import { + type ParseHelperOptions, + expectNoParserAndLexerErrors, + loadTestExtensions, + parseHelper, + readJvTestAssetHelper, +} from '@jvalue/jayvee-language-server/test'; +import { + type AstNode, + type AstNodeLocator, + type LangiumDocument, +} from 'langium'; +import { NodeFileSystem } from 'langium/node'; + +import { createWorkbookFromLocalExcelFile } from '../../test/util'; + +import { CellWriterExecutor } from './cell-writer-executor'; + +describe('Validation of CellWriterExecutor', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + let locator: AstNodeLocator; + let services: JayveeServices; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../test/assets/cell-writer-executor/', + ); + + async function readTestWorkbook(fileName: string): Promise<R.Workbook> { + const absoluteFileName = path.resolve( + __dirname, + '../../test/assets/cell-writer-executor/', + fileName, + ); + return await createWorkbookFromLocalExcelFile(absoluteFileName); + } + + async function parseAndExecuteExecutor( + input: string, + IOInput: R.Sheet, + ): Promise<R.Result<R.Sheet>> { + const document = await parse(input, { validation: true }); + expectNoParserAndLexerErrors(document); + + const block = locator.getAstNode<BlockDefinition>( + document.parseResult.value, + 'pipelines@0/blocks@1', + ) as BlockDefinition; + + return new CellWriterExecutor().doExecute( + IOInput, + getTestExecutionContext(locator, document, services, [block]), + ); + } + + beforeAll(async () => { + // Create language services + services = createJayveeServices(NodeFileSystem).Jayvee; + await loadTestExtensions(services, [ + path.resolve(__dirname, '../../test/test-extension/TestBlockTypes.jv'), + ]); + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + it('should diagnose no error on valid single cell writer', async () => { + const text = readJvTestAsset('valid-single-cell-writer.jv'); + + const testWorkbook = await readTestWorkbook('test-A1-C16.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.SHEET); + expect(result.right.getNumberOfColumns()).toEqual(3); + expect(result.right.getNumberOfRows()).toEqual(16); + expect(result.right.getHeaderRow()).toEqual(['16', 'Test', 'true']); + expect(result.right.getData()).toEqual( + expect.arrayContaining([ + ['16', 'Test', 'true'], + ['1', 'Test', 'false'], + ['15', 'Test', 'true'], + ]), + ); + } + }); + + it('should diagnose no error on valid cell range writer', async () => { + const text = readJvTestAsset('valid-cell-range-writer.jv'); + + const testWorkbook = await readTestWorkbook('test-A1-C16.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.SHEET); + expect(result.right.getNumberOfColumns()).toEqual(3); + expect(result.right.getNumberOfRows()).toEqual(16); + expect(result.right.getHeaderRow()).toEqual(['16', 'Test2', '']); + expect(result.right.getData()).toEqual( + expect.arrayContaining([ + ['16', 'Test2', ''], + ['1', 'Test', 'false'], + ['15', 'Test', 'true'], + ]), + ); + } + }); + + it('should diagnose error on single cell writer on empty sheet', async () => { + const text = readJvTestAsset('valid-single-cell-writer.jv'); + + const testWorkbook = await readTestWorkbook('test-empty.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isOk(result)).toEqual(false); + if (R.isErr(result)) { + expect(result.left.message).toEqual( + 'Some specified cells do not exist in the sheet', + ); + } + }); +}); diff --git a/libs/extensions/tabular/exec/src/lib/cell-writer-executor.ts b/libs/extensions/tabular/exec/src/lib/cell-writer-executor.ts new file mode 100644 index 00000000..08199135 --- /dev/null +++ b/libs/extensions/tabular/exec/src/lib/cell-writer-executor.ts @@ -0,0 +1,91 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; + +import * as R from '@jvalue/jayvee-execution'; +import { + AbstractBlockExecutor, + type BlockExecutorClass, + type ExecutionContext, + type Sheet, + implementsStatic, +} from '@jvalue/jayvee-execution'; +import { IOType } from '@jvalue/jayvee-language-server'; + +@implementsStatic<BlockExecutorClass>() +export class CellWriterExecutor extends AbstractBlockExecutor< + IOType.SHEET, + IOType.SHEET +> { + public static readonly type = 'CellWriter'; + + constructor() { + super(IOType.SHEET, IOType.SHEET); + } + + // eslint-disable-next-line @typescript-eslint/require-await + async doExecute( + inputSheet: Sheet, + context: ExecutionContext, + ): Promise<R.Result<Sheet>> { + const relativeCellRange = context.getPropertyValue( + 'at', + context.valueTypeProvider.Primitives.CellRange, + ); + const writeValues = context.getPropertyValue( + 'write', + context.valueTypeProvider.createCollectionValueTypeOf( + context.valueTypeProvider.Primitives.Text, + ), + ); + + const relativeCellRangeWrapper = + context.wrapperFactories.CellRange.wrap(relativeCellRange); + + assert(relativeCellRangeWrapper.isOneDimensional()); + + const absoluteCellRange = inputSheet.resolveRelativeIndexes( + relativeCellRangeWrapper, + ); + if (!inputSheet.isInBounds(absoluteCellRange)) { + return R.err({ + message: 'Some specified cells do not exist in the sheet', + diagnostic: { node: absoluteCellRange.astNode }, + }); + } + + const cellIndexesToWrite = inputSheet.enumerateCellIndexes( + relativeCellRangeWrapper, + ); + + if (writeValues.length !== cellIndexesToWrite.length) { + context.logger.logWarnDiagnostic( + `The number of values to write (${writeValues.length}) does not match the number of cells (${cellIndexesToWrite.length})`, + { node: relativeCellRangeWrapper.astNode }, + ); + } + + const resultingSheet = inputSheet.clone(); + for ( + let i = 0; + i < Math.min(writeValues.length, cellIndexesToWrite.length); + ++i + ) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const cellIndex = cellIndexesToWrite[i]!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const writeValue = writeValues[i]!; + + context.logger.logDebug( + `Writing "${writeValue}" at cell ${cellIndex.toString()}`, + ); + + resultingSheet.writeCell(cellIndex, writeValue); + } + + return R.ok(resultingSheet); + } +} diff --git a/libs/extensions/tabular/exec/src/lib/column-deleter-executor.spec.ts b/libs/extensions/tabular/exec/src/lib/column-deleter-executor.spec.ts new file mode 100644 index 00000000..ca80bda8 --- /dev/null +++ b/libs/extensions/tabular/exec/src/lib/column-deleter-executor.spec.ts @@ -0,0 +1,172 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import path from 'node:path'; + +import * as R from '@jvalue/jayvee-execution'; +import { getTestExecutionContext } from '@jvalue/jayvee-execution/test'; +import { + type BlockDefinition, + IOType, + type JayveeServices, + createJayveeServices, +} from '@jvalue/jayvee-language-server'; +import { + type ParseHelperOptions, + expectNoParserAndLexerErrors, + loadTestExtensions, + parseHelper, + readJvTestAssetHelper, +} from '@jvalue/jayvee-language-server/test'; +import { + type AstNode, + type AstNodeLocator, + type LangiumDocument, +} from 'langium'; +import { NodeFileSystem } from 'langium/node'; + +import { createWorkbookFromLocalExcelFile } from '../../test/util'; + +import { ColumnDeleterExecutor } from './column-deleter-executor'; + +describe('Validation of ColumnDeleterExecutor', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + let locator: AstNodeLocator; + let services: JayveeServices; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../test/assets/column-deleter-executor/', + ); + + async function readTestWorkbook(fileName: string): Promise<R.Workbook> { + const absoluteFileName = path.resolve( + __dirname, + '../../test/assets/column-deleter-executor/', + fileName, + ); + return await createWorkbookFromLocalExcelFile(absoluteFileName); + } + + async function parseAndExecuteExecutor( + input: string, + IOInput: R.Sheet, + ): Promise<R.Result<R.Sheet>> { + const document = await parse(input, { validation: true }); + expectNoParserAndLexerErrors(document); + + const block = locator.getAstNode<BlockDefinition>( + document.parseResult.value, + 'pipelines@0/blocks@1', + ) as BlockDefinition; + + return new ColumnDeleterExecutor().doExecute( + IOInput, + getTestExecutionContext(locator, document, services, [block]), + ); + } + + beforeAll(async () => { + // Create language services + services = createJayveeServices(NodeFileSystem).Jayvee; + await loadTestExtensions(services, [ + path.resolve(__dirname, '../../test/test-extension/TestBlockTypes.jv'), + ]); + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + it('should diagnose no error on valid single column deleter', async () => { + const text = readJvTestAsset('valid-single-column-deleter.jv'); + + const testWorkbook = await readTestWorkbook('test-A1-C16.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.SHEET); + expect(result.right.getNumberOfColumns()).toEqual(2); + expect(result.right.getNumberOfRows()).toEqual(16); + expect(result.right.getHeaderRow()).toEqual(['Test', 'true']); + expect(result.right.getData()).toEqual( + expect.arrayContaining([ + ['Test', 'true'], + ['Test', 'false'], + ['Test', 'true'], + ]), + ); + } + }); + + it('should diagnose no error on valid multiple column deleter', async () => { + const text = readJvTestAsset('valid-multiple-column-deleter.jv'); + + const testWorkbook = await readTestWorkbook('test-A1-C16.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.SHEET); + expect(result.right.getNumberOfColumns()).toEqual(1); + expect(result.right.getNumberOfRows()).toEqual(16); + expect(result.right.getHeaderRow()).toEqual(['Test']); + expect(result.right.getData()).toEqual( + expect.arrayContaining([['Test'], ['Test'], ['Test']]), + ); + } + }); + + it('should diagnose error on deleting non existing column', async () => { + const text = readJvTestAsset('valid-multiple-column-deleter.jv'); + + const testWorkbook = await readTestWorkbook('test-A1-B2.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isOk(result)).toEqual(false); + if (R.isErr(result)) { + expect(result.left.message).toEqual( + 'The specified column C does not exist in the sheet', + ); + } + }); + + it('should diagnose only one column deletion on duplicate columns', async () => { + const text = readJvTestAsset('valid-duplicate-column.jv'); + + const testWorkbook = await readTestWorkbook('test-A1-C16.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.SHEET); + expect(result.right.getNumberOfColumns()).toEqual(2); + expect(result.right.getNumberOfRows()).toEqual(16); + expect(result.right.getHeaderRow()).toEqual(['Test', 'true']); + expect(result.right.getData()).toEqual( + expect.arrayContaining([ + ['Test', 'true'], + ['Test', 'false'], + ['Test', 'true'], + ]), + ); + } + }); +}); diff --git a/libs/extensions/tabular/exec/src/lib/column-deleter-executor.ts b/libs/extensions/tabular/exec/src/lib/column-deleter-executor.ts new file mode 100644 index 00000000..53b7f0d8 --- /dev/null +++ b/libs/extensions/tabular/exec/src/lib/column-deleter-executor.ts @@ -0,0 +1,108 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; + +import * as R from '@jvalue/jayvee-execution'; +import { + AbstractBlockExecutor, + type BlockExecutorClass, + type ExecutionContext, + type Sheet, + implementsStatic, +} from '@jvalue/jayvee-execution'; +import { + type ColumnWrapper, + IOType, + columnIndexToString, + getColumnIndex, + isColumnWrapper, +} from '@jvalue/jayvee-language-server'; + +@implementsStatic<BlockExecutorClass>() +export class ColumnDeleterExecutor extends AbstractBlockExecutor< + IOType.SHEET, + IOType.SHEET +> { + public static readonly type = 'ColumnDeleter'; + + constructor() { + super(IOType.SHEET, IOType.SHEET); + } + + // eslint-disable-next-line @typescript-eslint/require-await + async doExecute( + inputSheet: Sheet, + context: ExecutionContext, + ): Promise<R.Result<Sheet>> { + const relativeColumns = context + .getPropertyValue( + 'delete', + context.valueTypeProvider.createCollectionValueTypeOf( + context.valueTypeProvider.Primitives.CellRange, + ), + ) + .map((astNode) => context.wrapperFactories.CellRange.wrap(astNode)); + assert(relativeColumns.every(isColumnWrapper)); + + let absoluteColumns = relativeColumns.map((column) => + inputSheet.resolveRelativeIndexes(column), + ); + + for (const column of absoluteColumns) { + if (!inputSheet.isInBounds(column)) { + const columnIndex = getColumnIndex(column); + return R.err({ + message: `The specified column ${columnIndexToString( + columnIndex, + )} does not exist in the sheet`, + diagnostic: { node: column.astNode }, + }); + } + } + + // Required for removing duplicates in the next step + this.sortByColumnIndex(absoluteColumns); + + // That way, the upcoming deletion is only called once per individual column + absoluteColumns = this.removeDuplicateColumns(absoluteColumns); + + context.logger.logDebug( + `Deleting column(s) ${absoluteColumns + .map(getColumnIndex) + .map(columnIndexToString) + .join(', ')}`, + ); + + // By reversing the order, the column indexes stay stable during deletion + absoluteColumns.reverse(); + + const resultingSheet = inputSheet.clone(); + absoluteColumns.forEach((column) => { + resultingSheet.deleteColumn(column); + }); + + return R.ok(resultingSheet); + } + + private sortByColumnIndex(columns: ColumnWrapper[]): void { + columns.sort( + (columnA, columnB) => getColumnIndex(columnA) - getColumnIndex(columnB), + ); + } + + private removeDuplicateColumns(columns: ColumnWrapper[]): ColumnWrapper[] { + return columns.reduce<ColumnWrapper[]>((previous, column, index) => { + const previousColumn = previous[index - 1]; + if (previousColumn !== undefined) { + if (getColumnIndex(previousColumn) === getColumnIndex(column)) { + // The current column is a duplicate because it has the same index as the previous column + return previous; + } + } + return [...previous, column]; + }, []); + } +} diff --git a/libs/extensions/tabular/exec/src/lib/csv-interpreter-executor.spec.ts b/libs/extensions/tabular/exec/src/lib/csv-interpreter-executor.spec.ts new file mode 100644 index 00000000..47be1e61 --- /dev/null +++ b/libs/extensions/tabular/exec/src/lib/csv-interpreter-executor.spec.ts @@ -0,0 +1,103 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import path from 'node:path'; + +import * as R from '@jvalue/jayvee-execution'; +import { + createTextFileFromLocalFile, + getTestExecutionContext, +} from '@jvalue/jayvee-execution/test'; +import { + type BlockDefinition, + IOType, + type JayveeServices, + createJayveeServices, +} from '@jvalue/jayvee-language-server'; +import { + type ParseHelperOptions, + expectNoParserAndLexerErrors, + loadTestExtensions, + parseHelper, + readJvTestAssetHelper, +} from '@jvalue/jayvee-language-server/test'; +import { + type AstNode, + type AstNodeLocator, + type LangiumDocument, +} from 'langium'; +import { NodeFileSystem } from 'langium/node'; + +import { CSVInterpreterExecutor } from './csv-interpreter-executor'; + +describe('Validation of CSVInterpreterExecutor', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + let locator: AstNodeLocator; + let services: JayveeServices; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../test/assets/csv-interpreter-executor/', + ); + + function readTestFile(fileName: string): R.TextFile { + const absoluteFileName = path.resolve( + __dirname, + '../../test/assets/csv-interpreter-executor/', + fileName, + ); + return createTextFileFromLocalFile(absoluteFileName); + } + + async function parseAndExecuteExecutor( + input: string, + IOInput: R.TextFile, + ): Promise<R.Result<R.Sheet>> { + const document = await parse(input, { validation: true }); + expectNoParserAndLexerErrors(document); + + const block = locator.getAstNode<BlockDefinition>( + document.parseResult.value, + 'pipelines@0/blocks@1', + ) as BlockDefinition; + + return new CSVInterpreterExecutor().doExecute( + IOInput, + getTestExecutionContext(locator, document, services, [block]), + ); + } + + beforeAll(async () => { + // Create language services + services = createJayveeServices(NodeFileSystem).Jayvee; + await loadTestExtensions(services, [ + path.resolve(__dirname, '../../test/test-extension/TestBlockTypes.jv'), + ]); + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + it('should diagnose no error on valid csv file', async () => { + const text = readJvTestAsset('valid-csv-interpreter.jv'); + + const testCsv = readTestFile('valid-csv.csv'); + const result = await parseAndExecuteExecutor(text, testCsv); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.SHEET); + expect(result.right.getNumberOfColumns()).toEqual(2); + expect(result.right.getNumberOfRows()).toEqual(2); + expect(result.right.getData()).toEqual([ + ['Test', 'true'], + ['Test', 'false'], + ]); + } + }); +}); diff --git a/libs/extensions/tabular/exec/src/lib/csv-interpreter-executor.ts b/libs/extensions/tabular/exec/src/lib/csv-interpreter-executor.ts new file mode 100644 index 00000000..6671f964 --- /dev/null +++ b/libs/extensions/tabular/exec/src/lib/csv-interpreter-executor.ts @@ -0,0 +1,108 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { parseString as parseStringAsCsv } from '@fast-csv/parse'; +import { type ParserOptionsArgs } from '@fast-csv/parse/build/src/ParserOptions'; +import * as R from '@jvalue/jayvee-execution'; +import { + AbstractBlockExecutor, + type BlockExecutorClass, + type ExecutionContext, + Sheet, + type TextFile, + implementsStatic, +} from '@jvalue/jayvee-execution'; +import { IOType } from '@jvalue/jayvee-language-server'; +import { either as E } from 'fp-ts'; + +@implementsStatic<BlockExecutorClass>() +export class CSVInterpreterExecutor extends AbstractBlockExecutor< + IOType.TEXT_FILE, + IOType.SHEET +> { + public static readonly type = 'CSVInterpreter'; + + constructor() { + super(IOType.TEXT_FILE, IOType.SHEET); + } + + async doExecute( + file: TextFile, + context: ExecutionContext, + ): Promise<R.Result<Sheet>> { + const delimiter = context.getPropertyValue( + 'delimiter', + context.valueTypeProvider.Primitives.Text, + ); + const enclosing = context.getPropertyValue( + 'enclosing', + context.valueTypeProvider.Primitives.Text, + ); + const enclosingEscape = context.getPropertyValue( + 'enclosingEscape', + context.valueTypeProvider.Primitives.Text, + ); + + context.logger.logDebug( + `Parsing raw data as CSV using delimiter "${delimiter}"`, + ); + + const parseOptions: ParserOptionsArgs = { + delimiter, + quote: enclosing, + escape: enclosingEscape, + }; + const csvData = await parseAsCsv(file.content, parseOptions); + + if (E.isLeft(csvData)) { + return Promise.resolve( + R.err({ + message: `CSV parse failed in line ${csvData.left.lineNumber}: ${csvData.left.error.message}`, + diagnostic: { node: context.getCurrentNode(), property: 'name' }, + }), + ); + } + const sheet = new Sheet(csvData.right); + + context.logger.logDebug(`Parsing raw data as CSV-sheet successful`); + return Promise.resolve(R.ok(sheet)); + } +} + +async function parseAsCsv( + lines: string[], + parseOptions: ParserOptionsArgs, +): Promise<E.Either<{ error: Error; lineNumber: number }, string[][]>> { + let lineNumber = 1; + const rows: string[][] = []; + for await (const line of lines) { + const rowParseResult = await parseLineAsRow(line, parseOptions); + if (E.isLeft(rowParseResult)) { + return E.left({ error: rowParseResult.left, lineNumber }); + } + rows.push(rowParseResult.right); + + ++lineNumber; + } + return E.right(rows); +} + +async function parseLineAsRow( + line: string, + parseOptions: ParserOptionsArgs, +): Promise<E.Either<Error, string[]>> { + return new Promise((resolve) => { + let row: string[]; + parseStringAsCsv(line, parseOptions) + .on('data', (data: string[]) => { + row = data; + }) + .on('error', (error) => { + resolve(E.left(error)); + }) + .on('end', () => { + resolve(E.right(row)); + }); + }); +} diff --git a/libs/extensions/tabular/exec/src/lib/file-to-table-interpreter-executor.ts b/libs/extensions/tabular/exec/src/lib/file-to-table-interpreter-executor.ts new file mode 100644 index 00000000..9b63d3ae --- /dev/null +++ b/libs/extensions/tabular/exec/src/lib/file-to-table-interpreter-executor.ts @@ -0,0 +1,189 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { strict as assert } from 'assert'; + +import * as R from '@jvalue/jayvee-execution'; +import { + AbstractBlockExecutor, + type BlockExecutorClass, + type ExecutionContext, + PolarsTable, + implementsStatic, +} from '@jvalue/jayvee-execution'; +import { + IOType, + type ValueType, + type ValuetypeAssignment, +} from '@jvalue/jayvee-language-server'; +import pl, { + type DataType as PlDType, + type ReadCsvOptions, +} from 'nodejs-polars'; + +import { toPolarsDataTypeWithLogs } from './table-interpreter-executor'; + +export interface ColumnDefinitionEntry { + sheetColumnIndex: number; + columnName: string; + valueType: ValueType; + astNode: ValuetypeAssignment; +} + +@implementsStatic<BlockExecutorClass>() +export class FileToTableInterpreterExecutor extends AbstractBlockExecutor< + IOType.FILE, + IOType.TABLE +> { + public static readonly type = 'FileToTableInterpreter'; + + constructor() { + super(IOType.FILE, IOType.TABLE); + } + + protected colsAndSchema(context: ExecutionContext): { + columnNames: string[]; + schema: Record<string, PlDType>; + } { + const vtasss = context.getPropertyValue( + 'columns', + context.valueTypeProvider.createCollectionValueTypeOf( + context.valueTypeProvider.Primitives.ValuetypeAssignment, + ), + ); + const colDefs = this.deriveColumnDefinitionEntriesWithoutHeader( + vtasss, + context, + ); + + const schema: Record<string, PlDType> = {}; + const columnNames = colDefs.map((colDef) => { + const dt = toPolarsDataTypeWithLogs(colDef.valueType, context.logger); + if (dt === undefined) { + throw new Error( + `${colDef.valueType.getName()} is not supported in tables`, + ); + } + schema[colDef.columnName] = dt; + return colDef.columnName; + }); + return { + columnNames: columnNames, + schema: schema, + }; + } + protected csvOptions(context: ExecutionContext): Partial<ReadCsvOptions> { + const header = context.getPropertyValue( + 'header', + context.valueTypeProvider.Primitives.Boolean, + ); + const encoding = context.getPropertyValue( + 'encoding', + context.valueTypeProvider.Primitives.Text, + ); + if (encoding !== 'utf-8') { + throw new Error(`encoding ${encoding} Only utf8 is supported`); + } + + const delimiter = context.getPropertyValue( + 'delimiter', + context.valueTypeProvider.Primitives.Text, + ); + const enclosing = context.getPropertyValue( + 'enclosing', + context.valueTypeProvider.Primitives.Text, + ); + const { columnNames, schema } = this.colsAndSchema(context); + return { + columns: columnNames, + hasHeader: header, + encoding: 'utf8', + sep: delimiter, + quoteChar: enclosing, + schema: schema, + }; + } + + // eslint-disable-next-line @typescript-eslint/require-await + override async doExecute( + file: R.BinaryFile, + context: R.ExecutionContext, + ): Promise<R.Result<R.Table>> { + context.logger.logDebug(`Validating row(s) according to the column types`); + + const resultingTable = this.constructAndValidateTable( + Buffer.from(file.content), + this.csvOptions(context), + context, + ); + context.logger.logDebug( + `Validation completed, the resulting table has ${resultingTable.getNumberOfRows()} row(s) and ${resultingTable.getNumberOfColumns()} column(s)`, + ); + return R.ok(resultingTable); + } + + protected constructAndValidateTable( + content: Buffer, + options: Partial<ReadCsvOptions>, + context: ExecutionContext, + ): PolarsTable { + context.logger.logDebug(JSON.stringify(options.schema)); + const df = pl.readCSV(content, options); + return new PolarsTable(df); + } + + protected deriveColumnDefinitionEntriesWithoutHeader( + columnDefinitions: ValuetypeAssignment[], + context: ExecutionContext, + ): ColumnDefinitionEntry[] { + return columnDefinitions.map<ColumnDefinitionEntry>( + (columnDefinition, columnDefinitionIndex) => { + const columnValuetype = context.wrapperFactories.ValueType.wrap( + columnDefinition.type, + ); + assert(columnValuetype !== undefined); + return { + sheetColumnIndex: columnDefinitionIndex, + columnName: columnDefinition.name, + valueType: columnValuetype, + astNode: columnDefinition, + }; + }, + ); + } + + protected deriveColumnDefinitionEntriesFromHeader( + columnDefinitions: ValuetypeAssignment[], + headerRow: string[], + context: ExecutionContext, + ): ColumnDefinitionEntry[] { + context.logger.logDebug(`Matching header with provided column names`); + + const columnEntries: ColumnDefinitionEntry[] = []; + for (const columnDefinition of columnDefinitions) { + const indexOfMatchingHeader = headerRow.findIndex( + (headerColumnName) => headerColumnName === columnDefinition.name, + ); + if (indexOfMatchingHeader === -1) { + context.logger.logDebug( + `Omitting column "${columnDefinition.name}" as the name was not found in the header`, + ); + continue; + } + const columnValuetype = context.wrapperFactories.ValueType.wrap( + columnDefinition.type, + ); + assert(columnValuetype !== undefined); + + columnEntries.push({ + sheetColumnIndex: indexOfMatchingHeader, + columnName: columnDefinition.name, + valueType: columnValuetype, + astNode: columnDefinition, + }); + } + + return columnEntries; + } +} diff --git a/libs/extensions/tabular/exec/src/lib/row-deleter-executor.spec.ts b/libs/extensions/tabular/exec/src/lib/row-deleter-executor.spec.ts new file mode 100644 index 00000000..44d9106e --- /dev/null +++ b/libs/extensions/tabular/exec/src/lib/row-deleter-executor.spec.ts @@ -0,0 +1,167 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import path from 'node:path'; + +import * as R from '@jvalue/jayvee-execution'; +import { getTestExecutionContext } from '@jvalue/jayvee-execution/test'; +import { + type BlockDefinition, + IOType, + type JayveeServices, + createJayveeServices, +} from '@jvalue/jayvee-language-server'; +import { + type ParseHelperOptions, + expectNoParserAndLexerErrors, + loadTestExtensions, + parseHelper, + readJvTestAssetHelper, +} from '@jvalue/jayvee-language-server/test'; +import { + type AstNode, + type AstNodeLocator, + type LangiumDocument, +} from 'langium'; +import { NodeFileSystem } from 'langium/node'; + +import { createWorkbookFromLocalExcelFile } from '../../test/util'; + +import { RowDeleterExecutor } from './row-deleter-executor'; + +describe('Validation of RowDeleterExecutor', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + let locator: AstNodeLocator; + let services: JayveeServices; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../test/assets/row-deleter-executor/', + ); + + async function readTestWorkbook(fileName: string): Promise<R.Workbook> { + const absoluteFileName = path.resolve( + __dirname, + '../../test/assets/row-deleter-executor/', + fileName, + ); + return await createWorkbookFromLocalExcelFile(absoluteFileName); + } + + async function parseAndExecuteExecutor( + input: string, + IOInput: R.Sheet, + ): Promise<R.Result<R.Sheet>> { + const document = await parse(input, { validation: true }); + expectNoParserAndLexerErrors(document); + + const block = locator.getAstNode<BlockDefinition>( + document.parseResult.value, + 'pipelines@0/blocks@1', + ) as BlockDefinition; + + return new RowDeleterExecutor().doExecute( + IOInput, + getTestExecutionContext(locator, document, services, [block]), + ); + } + + beforeAll(async () => { + // Create language services + services = createJayveeServices(NodeFileSystem).Jayvee; + await loadTestExtensions(services, [ + path.resolve(__dirname, '../../test/test-extension/TestBlockTypes.jv'), + ]); + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + it('should diagnose no error on valid single row deleter', async () => { + const text = readJvTestAsset('valid-single-row-deleter.jv'); + + const testWorkbook = await readTestWorkbook('test-A1-C16.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.SHEET); + expect(result.right.getNumberOfColumns()).toEqual(3); + expect(result.right.getNumberOfRows()).toEqual(15); + expect(result.right.getHeaderRow()).toEqual(['1', 'Test', 'false']); + expect(result.right.getData()).not.toEqual( + expect.arrayContaining([['0', 'Test', 'true']]), + ); + } + }); + + it('should diagnose no error on valid multiple row deleter', async () => { + const text = readJvTestAsset('valid-multiple-row-deleter.jv'); + + const testWorkbook = await readTestWorkbook('test-A1-C16.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.SHEET); + expect(result.right.getNumberOfColumns()).toEqual(3); + expect(result.right.getNumberOfRows()).toEqual(14); + expect(result.right.getHeaderRow()).toEqual(['1', 'Test', 'false']); + expect(result.right.getData()).not.toEqual( + expect.arrayContaining([ + ['0', 'Test', 'true'], + ['4', 'Test', 'true'], + ]), + ); + } + }); + + it('should diagnose error on deleting non existing row', async () => { + const text = readJvTestAsset('valid-multiple-row-deleter.jv'); + + const testWorkbook = await readTestWorkbook('test-A1-B2.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isOk(result)).toEqual(false); + if (R.isErr(result)) { + expect(result.left.message).toEqual( + 'The specified row 5 does not exist in the sheet', + ); + } + }); + + it('should diagnose only one row deletion on duplicate rows', async () => { + const text = readJvTestAsset('valid-duplicate-row.jv'); + + const testWorkbook = await readTestWorkbook('test-A1-C16.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.SHEET); + expect(result.right.getNumberOfColumns()).toEqual(3); + expect(result.right.getNumberOfRows()).toEqual(15); + expect(result.right.getHeaderRow()).toEqual(['1', 'Test', 'false']); + expect(result.right.getData()).not.toEqual( + expect.arrayContaining([['0', 'Test', 'true']]), + ); + } + }); +}); diff --git a/libs/extensions/tabular/exec/src/lib/row-deleter-executor.ts b/libs/extensions/tabular/exec/src/lib/row-deleter-executor.ts new file mode 100644 index 00000000..f0509a03 --- /dev/null +++ b/libs/extensions/tabular/exec/src/lib/row-deleter-executor.ts @@ -0,0 +1,108 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; + +import * as R from '@jvalue/jayvee-execution'; +import { + AbstractBlockExecutor, + type BlockExecutorClass, + type ExecutionContext, + type Sheet, + implementsStatic, +} from '@jvalue/jayvee-execution'; +import { + IOType, + type RowWrapper, + getRowIndex, + isRowWrapper, + rowIndexToString, +} from '@jvalue/jayvee-language-server'; + +@implementsStatic<BlockExecutorClass>() +export class RowDeleterExecutor extends AbstractBlockExecutor< + IOType.SHEET, + IOType.SHEET +> { + public static readonly type = 'RowDeleter'; + + constructor() { + super(IOType.SHEET, IOType.SHEET); + } + + // eslint-disable-next-line @typescript-eslint/require-await + async doExecute( + inputSheet: Sheet, + context: ExecutionContext, + ): Promise<R.Result<Sheet>> { + const relativeRows = context + .getPropertyValue( + 'delete', + context.valueTypeProvider.createCollectionValueTypeOf( + context.valueTypeProvider.Primitives.CellRange, + ), + ) + .map((astNode) => context.wrapperFactories.CellRange.wrap(astNode)); + assert(relativeRows.every(isRowWrapper)); + + let absoluteRows = relativeRows.map((row) => + inputSheet.resolveRelativeIndexes(row), + ); + + for (const row of absoluteRows) { + if (!inputSheet.isInBounds(row)) { + const rowIndex = getRowIndex(row); + return R.err({ + message: `The specified row ${rowIndexToString( + rowIndex, + )} does not exist in the sheet`, + diagnostic: { node: row.astNode }, + }); + } + } + + // Required for removing duplicates in the next step + this.sortByRowIndex(absoluteRows); + + // That way, the upcoming deletion is only called once per individual row + absoluteRows = this.removeDuplicateRows(absoluteRows); + + context.logger.logDebug( + `Deleting row(s) ${absoluteRows + .map(getRowIndex) + .map(rowIndexToString) + .join(', ')}`, + ); + + // By reversing the order, the row indexes stay stable during deletion + absoluteRows.reverse(); + + const resultingSheet = inputSheet.clone(); + absoluteRows.forEach((row) => { + resultingSheet.deleteRow(row); + }); + + return R.ok(resultingSheet); + } + + private sortByRowIndex(rows: RowWrapper[]): void { + rows.sort( + (firstRow, secondRow) => getRowIndex(firstRow) - getRowIndex(secondRow), + ); + } + + private removeDuplicateRows(rows: RowWrapper[]): RowWrapper[] { + return rows.reduce<RowWrapper[]>((previous, row, index) => { + const previousRow = previous[index - 1]; + if (previousRow !== undefined) { + if (getRowIndex(previousRow) === getRowIndex(row)) { + // The current row is a duplicate because it has the same index as the previous row + return previous; + } + } + return [...previous, row]; + }, []); + } +} diff --git a/libs/extensions/tabular/exec/src/lib/sheet-picker-executor.spec.ts b/libs/extensions/tabular/exec/src/lib/sheet-picker-executor.spec.ts new file mode 100644 index 00000000..d656e161 --- /dev/null +++ b/libs/extensions/tabular/exec/src/lib/sheet-picker-executor.spec.ts @@ -0,0 +1,120 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import path from 'node:path'; + +import * as R from '@jvalue/jayvee-execution'; +import { getTestExecutionContext } from '@jvalue/jayvee-execution/test'; +import { + type BlockDefinition, + IOType, + type JayveeServices, + createJayveeServices, +} from '@jvalue/jayvee-language-server'; +import { + type ParseHelperOptions, + expectNoParserAndLexerErrors, + loadTestExtensions, + parseHelper, + readJvTestAssetHelper, +} from '@jvalue/jayvee-language-server/test'; +import { + type AstNode, + type AstNodeLocator, + type LangiumDocument, +} from 'langium'; +import { NodeFileSystem } from 'langium/node'; + +import { createWorkbookFromLocalExcelFile } from '../../test/util'; + +import { SheetPickerExecutor } from './sheet-picker-executor'; + +describe('Validation of SheetPickerExecutor', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + let locator: AstNodeLocator; + let services: JayveeServices; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../test/assets/sheet-picker-executor/', + ); + + async function readTestWorkbook(fileName: string): Promise<R.Workbook> { + const absoluteFileName = path.resolve( + __dirname, + '../../test/assets/sheet-picker-executor/', + fileName, + ); + return await createWorkbookFromLocalExcelFile(absoluteFileName); + } + + async function parseAndExecuteExecutor( + input: string, + IOInput: R.Workbook, + ): Promise<R.Result<R.Sheet>> { + const document = await parse(input, { validation: true }); + expectNoParserAndLexerErrors(document); + + const block = locator.getAstNode<BlockDefinition>( + document.parseResult.value, + 'pipelines@0/blocks@1', + ) as BlockDefinition; + + return new SheetPickerExecutor().doExecute( + IOInput, + getTestExecutionContext(locator, document, services, [block]), + ); + } + + beforeAll(async () => { + // Create language services + services = createJayveeServices(NodeFileSystem).Jayvee; + await loadTestExtensions(services, [ + path.resolve(__dirname, '../../test/test-extension/TestBlockTypes.jv'), + ]); + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + it('should diagnose no error on valid workbook', async () => { + const text = readJvTestAsset('valid-sheet-picker.jv'); + + const testWorkbook = await readTestWorkbook('test-A1-C16.xlsx'); + const result = await parseAndExecuteExecutor(text, testWorkbook); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.SHEET); + expect(result.right.getNumberOfColumns()).toEqual(3); + expect(result.right.getNumberOfRows()).toEqual(16); + expect(result.right.getHeaderRow()).toEqual(['0', 'Test', 'true']); + expect(result.right.getData()).toEqual( + expect.arrayContaining([ + ['0', 'Test', 'true'], + ['1', 'Test', 'false'], + ['15', 'Test', 'true'], + ]), + ); + } + }); + + it('should diagnose error on sheet not found', async () => { + const text = readJvTestAsset('valid-custom-sheet-name.jv'); + + const testWorkbook = await readTestWorkbook('test-A1-C16.xlsx'); + const result = await parseAndExecuteExecutor(text, testWorkbook); + + expect(R.isOk(result)).toEqual(false); + if (R.isErr(result)) { + expect(result.left.message).toEqual( + 'Workbook does not contain a sheet named MyCustomSheet', + ); + } + }); +}); diff --git a/libs/extensions/tabular/exec/src/lib/sheet-picker-executor.ts b/libs/extensions/tabular/exec/src/lib/sheet-picker-executor.ts new file mode 100644 index 00000000..e5b0cdfa --- /dev/null +++ b/libs/extensions/tabular/exec/src/lib/sheet-picker-executor.ts @@ -0,0 +1,45 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import * as R from '@jvalue/jayvee-execution'; +import { + AbstractBlockExecutor, + type BlockExecutorClass, + type ExecutionContext, + type Sheet, + type Workbook, + implementsStatic, +} from '@jvalue/jayvee-execution'; +import { IOType } from '@jvalue/jayvee-language-server'; + +@implementsStatic<BlockExecutorClass>() +export class SheetPickerExecutor extends AbstractBlockExecutor< + IOType.WORKBOOK, + IOType.SHEET +> { + public static readonly type = 'SheetPicker'; + + constructor() { + super(IOType.WORKBOOK, IOType.SHEET); + } + + // eslint-disable-next-line @typescript-eslint/require-await + async doExecute( + workbook: Workbook, + context: ExecutionContext, + ): Promise<R.Result<Sheet>> { + const sheetName = context.getPropertyValue( + 'sheetName', + context.valueTypeProvider.Primitives.Text, + ); + const sheet = workbook.getSheetByName(sheetName); + if (sheet === undefined) { + return R.err({ + message: `Workbook does not contain a sheet named ${sheetName}`, + diagnostic: { node: context.getCurrentNode(), property: 'name' }, + }); + } + return R.ok(sheet); + } +} diff --git a/libs/extensions/tabular/exec/src/lib/table-interpreter-executor.spec.ts b/libs/extensions/tabular/exec/src/lib/table-interpreter-executor.spec.ts new file mode 100644 index 00000000..cf41dfec --- /dev/null +++ b/libs/extensions/tabular/exec/src/lib/table-interpreter-executor.spec.ts @@ -0,0 +1,303 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import path from 'node:path'; + +import * as R from '@jvalue/jayvee-execution'; +import { getTestExecutionContext } from '@jvalue/jayvee-execution/test'; +import { + type BlockDefinition, + IOType, + type JayveeServices, + createJayveeServices, +} from '@jvalue/jayvee-language-server'; +import { + type ParseHelperOptions, + expectNoParserAndLexerErrors, + loadTestExtensions, + parseHelper, + readJvTestAssetHelper, +} from '@jvalue/jayvee-language-server/test'; +import { + type AstNode, + type AstNodeLocator, + type LangiumDocument, +} from 'langium'; +import { NodeFileSystem } from 'langium/node'; + +import { createWorkbookFromLocalExcelFile } from '../../test/util'; + +import { TableInterpreterExecutor } from './table-interpreter-executor'; + +describe('Validation of TableInterpreterExecutor', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + let locator: AstNodeLocator; + let services: JayveeServices; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../test/assets/table-interpreter-executor/', + ); + + async function readTestWorkbook(fileName: string): Promise<R.Workbook> { + const absoluteFileName = path.resolve( + __dirname, + '../../test/assets/table-interpreter-executor/', + fileName, + ); + return await createWorkbookFromLocalExcelFile(absoluteFileName); + } + + async function parseAndExecuteExecutor( + input: string, + IOInput: R.Sheet, + ): Promise<R.Result<R.Table>> { + const document = await parse(input, { validation: true }); + expectNoParserAndLexerErrors(document); + + const block = locator.getAstNode<BlockDefinition>( + document.parseResult.value, + 'pipelines@0/blocks@1', + ) as BlockDefinition; + + return new TableInterpreterExecutor().doExecute( + IOInput, + getTestExecutionContext(locator, document, services, [block]), + ); + } + + beforeAll(async () => { + // Create language services + services = createJayveeServices(NodeFileSystem).Jayvee; + await loadTestExtensions(services, [ + path.resolve(__dirname, '../../test/test-extension/TestBlockTypes.jv'), + ]); + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + describe('validation of sheet with header', () => { + it('should diagnose no error on valid sheet', async () => { + const text = readJvTestAsset('valid-with-header.jv'); + + const testWorkbook = await readTestWorkbook('test-with-header.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.TABLE); + expect(result.right.getNumberOfColumns()).toEqual(3); + expect(result.right.getNumberOfRows()).toEqual(16); + expect(result.right.getColumn('index')).toEqual( + expect.objectContaining({ + values: expect.arrayContaining([0, 1, 2, 15]) as number[], + }), + ); + expect(result.right.getColumn('name')).toEqual( + expect.objectContaining({ + values: expect.arrayContaining(['Test']) as string[], + }), + ); + expect(result.right.getColumn('flag')).toEqual( + expect.objectContaining({ + values: expect.arrayContaining([true, false]) as boolean[], + }), + ); + } + }); + + it('should diagnose empty table on empty column parameter', async () => { + const text = readJvTestAsset('valid-empty-columns-with-header.jv'); + + const testWorkbook = await readTestWorkbook('test-with-header.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.TABLE); + expect(result.right.getNumberOfColumns()).toEqual(0); + expect(result.right.getNumberOfRows()).toEqual(0); + } + }); + + it('should diagnose empty table on wrong header case', async () => { + const text = readJvTestAsset('valid-with-capitalized-header.jv'); + + const testWorkbook = await readTestWorkbook('test-with-header.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.TABLE); + expect(result.right.getNumberOfColumns()).toEqual(0); + expect(result.right.getNumberOfRows()).toEqual(0); + } + }); + + it('should diagnose error on empty sheet', async () => { + const text = readJvTestAsset('valid-with-header.jv'); + + const testWorkbook = await readTestWorkbook('test-empty.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isOk(result)).toEqual(false); + if (R.isErr(result)) { + expect(result.left.message).toEqual( + 'The input sheet is empty and thus has no header', + ); + } + }); + + it('should diagnose skipping row on wrong cell value type', async () => { + const text = readJvTestAsset('valid-wrong-value-type-with-header.jv'); + + const testWorkbook = await readTestWorkbook('test-with-header.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.TABLE); + expect(result.right.getNumberOfColumns()).toEqual(3); + expect(result.right.getNumberOfRows()).toEqual(0); + } + }); + }); + + describe('validation of sheet without header', () => { + it('should diagnose no error on valid sheet', async () => { + const text = readJvTestAsset('valid-without-header.jv'); + + const testWorkbook = await readTestWorkbook('test-without-header.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.TABLE); + expect(result.right.getNumberOfColumns()).toEqual(3); + expect(result.right.getNumberOfRows()).toEqual(16); + expect(result.right.getColumn('index')).toEqual( + expect.objectContaining({ + values: expect.arrayContaining([0, 1, 2, 15]) as number[], + }), + ); + expect(result.right.getColumn('name')).toEqual( + expect.objectContaining({ + values: expect.arrayContaining(['Test']) as string[], + }), + ); + expect(result.right.getColumn('flag')).toEqual( + expect.objectContaining({ + values: expect.arrayContaining([true, false]) as boolean[], + }), + ); + } + }); + + it('should diagnose no error on valid sheet with header', async () => { + const text = readJvTestAsset('valid-without-header.jv'); + + const testWorkbook = await readTestWorkbook('test-with-header.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.TABLE); + expect(result.right.getNumberOfColumns()).toEqual(3); + expect(result.right.getNumberOfRows()).toEqual(16); + expect(result.right.getColumn('index')).toEqual( + expect.objectContaining({ + values: expect.arrayContaining([0, 1, 2, 15]) as number[], + }), + ); + expect(result.right.getColumn('name')).toEqual( + expect.objectContaining({ + values: expect.arrayContaining(['Test']) as string[], + }), + ); + expect(result.right.getColumn('flag')).toEqual( + expect.objectContaining({ + values: expect.arrayContaining([true, false]) as boolean[], + }), + ); + } + }); + + it('should diagnose empty table on empty column parameter', async () => { + const text = readJvTestAsset('valid-empty-columns-without-header.jv'); + + const testWorkbook = await readTestWorkbook('test-without-header.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.TABLE); + expect(result.right.getNumberOfColumns()).toEqual(0); + expect(result.right.getNumberOfRows()).toEqual(0); + } + }); + + it('should diagnose error on empty sheet', async () => { + const text = readJvTestAsset('valid-without-header.jv'); + + const testWorkbook = await readTestWorkbook('test-empty.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isOk(result)).toEqual(false); + if (R.isErr(result)) { + expect(result.left.message).toEqual( + 'There are 3 column definitions but the input sheet only has 0 columns', + ); + } + }); + + it('should diagnose skipping row on wrong cell value type', async () => { + const text = readJvTestAsset('valid-wrong-value-type-without-header.jv'); + + const testWorkbook = await readTestWorkbook('test-without-header.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.TABLE); + expect(result.right.getNumberOfColumns()).toEqual(3); + expect(result.right.getNumberOfRows()).toEqual(0); + } + }); + }); +}); diff --git a/libs/extensions/tabular/exec/src/lib/table-interpreter-executor.ts b/libs/extensions/tabular/exec/src/lib/table-interpreter-executor.ts new file mode 100644 index 00000000..6f5bbd0c --- /dev/null +++ b/libs/extensions/tabular/exec/src/lib/table-interpreter-executor.ts @@ -0,0 +1,327 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; + +import * as R from '@jvalue/jayvee-execution'; +import { + AbstractBlockExecutor, + type BlockExecutorClass, + type ExecutionContext, + PolarsTable, + type Sheet, + type Table, + type TableRowMap, + TsTable, + implementsStatic, + isValidValueRepresentation, + parseValueToInternalRepresentation, +} from '@jvalue/jayvee-execution'; +import { + CellIndex, + IOType, + type InternalValueRepresentation, + type ValueType, + type ValuetypeAssignment, + rowIndexToString, +} from '@jvalue/jayvee-language-server'; +import pl, { type DataType as PlDType } from 'nodejs-polars'; + +export interface ColumnDefinitionEntry { + sheetColumnIndex: number; + columnName: string; + valueType: ValueType; + astNode: ValuetypeAssignment; +} + +export abstract class TableInterpeter extends AbstractBlockExecutor< + IOType.SHEET, + IOType.TABLE +> { + constructor() { + super(IOType.SHEET, IOType.TABLE); + } + + protected deriveColumnDefinitionEntriesWithoutHeader( + columnDefinitions: ValuetypeAssignment[], + context: ExecutionContext, + ): ColumnDefinitionEntry[] { + return columnDefinitions.map<ColumnDefinitionEntry>( + (columnDefinition, columnDefinitionIndex) => { + const columnValuetype = context.wrapperFactories.ValueType.wrap( + columnDefinition.type, + ); + assert(columnValuetype !== undefined); + return { + sheetColumnIndex: columnDefinitionIndex, + columnName: columnDefinition.name, + valueType: columnValuetype, + astNode: columnDefinition, + }; + }, + ); + } + + protected deriveColumnDefinitionEntriesFromHeader( + columnDefinitions: ValuetypeAssignment[], + headerRow: string[], + context: ExecutionContext, + ): ColumnDefinitionEntry[] { + context.logger.logDebug(`Matching header with provided column names`); + + const columnEntries: ColumnDefinitionEntry[] = []; + for (const columnDefinition of columnDefinitions) { + const indexOfMatchingHeader = headerRow.findIndex( + (headerColumnName) => headerColumnName === columnDefinition.name, + ); + if (indexOfMatchingHeader === -1) { + context.logger.logDebug( + `Omitting column "${columnDefinition.name}" as the name was not found in the header`, + ); + continue; + } + const columnValuetype = context.wrapperFactories.ValueType.wrap( + columnDefinition.type, + ); + assert(columnValuetype !== undefined); + + columnEntries.push({ + sheetColumnIndex: indexOfMatchingHeader, + columnName: columnDefinition.name, + valueType: columnValuetype, + astNode: columnDefinition, + }); + } + + return columnEntries; + } + + // eslint-disable-next-line @typescript-eslint/require-await + override async doExecute( + inputSheet: Sheet, + context: R.ExecutionContext, + ): Promise<R.Result<R.Table>> { + const header = context.getPropertyValue( + 'header', + context.valueTypeProvider.Primitives.Boolean, + ); + const columnDefinitions = context.getPropertyValue( + 'columns', + context.valueTypeProvider.createCollectionValueTypeOf( + context.valueTypeProvider.Primitives.ValuetypeAssignment, + ), + ); + + let columnEntries: ColumnDefinitionEntry[]; + + if (header) { + if (inputSheet.getNumberOfRows() < 1) { + return R.err({ + message: 'The input sheet is empty and thus has no header', + diagnostic: { + node: context.getOrFailProperty('header'), + }, + }); + } + + const headerRow = inputSheet.getHeaderRow(); + + columnEntries = this.deriveColumnDefinitionEntriesFromHeader( + columnDefinitions, + headerRow, + context, + ); + } else { + if (inputSheet.getNumberOfColumns() < columnDefinitions.length) { + return R.err({ + message: `There are ${ + columnDefinitions.length + } column definitions but the input sheet only has ${inputSheet.getNumberOfColumns()} columns`, + diagnostic: { + node: context.getOrFailProperty('columns'), + }, + }); + } + + columnEntries = this.deriveColumnDefinitionEntriesWithoutHeader( + columnDefinitions, + context, + ); + } + + const numberOfTableRows = header + ? inputSheet.getNumberOfRows() - 1 + : inputSheet.getNumberOfRows(); + context.logger.logDebug( + `Validating ${numberOfTableRows} row(s) according to the column types`, + ); + + const resultingTable = this.constructAndValidateTable( + inputSheet, + header, + columnEntries, + context, + ); + context.logger.logDebug( + `Validation completed, the resulting table has ${resultingTable.getNumberOfRows()} row(s) and ${resultingTable.getNumberOfColumns()} column(s)`, + ); + return R.ok(resultingTable); + } + + protected abstract constructAndValidateTable( + sheet: Sheet, + header: boolean, + columnEntries: ColumnDefinitionEntry[], + context: ExecutionContext, + ): Table; + + protected parseAndValidateValue( + value: string, + valueType: ValueType, + context: ExecutionContext, + ): InternalValueRepresentation | undefined { + const parsedValue = parseValueToInternalRepresentation(value, valueType); + if (parsedValue === undefined) { + return undefined; + } + + if (!isValidValueRepresentation(parsedValue, valueType, context)) { + return undefined; + } + return parsedValue; + } +} + +export function toPolarsDataTypeWithLogs( + vt: ValueType, + logger: R.Logger, +): PlDType | undefined { + const dt = vt.toPolarsDataType(); + if (dt === undefined) { + logger.logDebug( + `${vt.getName()} isnt primitive and thus not convertible to polars`, + ); + } + return dt; +} + +@implementsStatic<BlockExecutorClass>() +export class PolarsTableInterpreterExecutor extends TableInterpeter { + public static readonly type = 'PolarsTableInterpreter'; + + protected override constructAndValidateTable( + sheet: Sheet, + header: boolean, + columnEntries: ColumnDefinitionEntry[], + context: ExecutionContext, + ): PolarsTable { + const rows = header ? sheet.getData().slice(1) : sheet.getData(); + const series = columnEntries.map((cEntry) => + this.constructSeries(rows, cEntry, context), + ); + const df = pl.DataFrame(series); + return new PolarsTable(df); + } + + private constructSeries( + rows: readonly (readonly string[])[], + columnEntry: ColumnDefinitionEntry, + context: ExecutionContext, + ): pl.Series { + const dtype = + toPolarsDataTypeWithLogs(columnEntry.valueType, context.logger) || + pl.String; + const cData = rows.map((row) => { + const cell = row[columnEntry.sheetColumnIndex]; + if (cell === undefined) { + throw new Error('columnEntries had more elements than the sheet data'); + } + const vt = dtype.equals(pl.String) + ? context.valueTypeProvider.Primitives.Text + : columnEntry.valueType; + return this.parseAndValidateValue(cell, vt, context); + }); + return pl.Series(columnEntry.columnName, cData, dtype); + } +} + +@implementsStatic<BlockExecutorClass>() +export class TsTableInterpreterExecutor extends TableInterpeter { + public static readonly type = 'TsTableInterpreter'; + + protected override constructAndValidateTable( + sheet: Sheet, + header: boolean, + columnEntries: ColumnDefinitionEntry[], + context: ExecutionContext, + ): Table { + const table = new TsTable(); + + // add columns + columnEntries.forEach((columnEntry) => { + table.addColumn( + columnEntry.columnName, + new R.TsTableColumn(columnEntry.columnName, columnEntry.valueType), + ); + }); + + // add rows + sheet.iterateRows((sheetRow, sheetRowIndex) => { + if (header && sheetRowIndex === 0) { + return; + } + + const tableRow = this.constructAndValidateTableRow( + sheetRow, + sheetRowIndex, + columnEntries, + context, + ); + if (tableRow === undefined) { + context.logger.logDebug( + `Omitting row ${rowIndexToString(sheetRowIndex)}`, + ); + return; + } + table.addRow(tableRow); + }); + return table; + } + + private constructAndValidateTableRow( + sheetRow: string[], + sheetRowIndex: number, + columnEntries: ColumnDefinitionEntry[], + context: ExecutionContext, + ): TableRowMap | undefined { + let invalidRow = false; + const tableRow: TableRowMap = {}; + columnEntries.forEach((columnEntry) => { + const sheetColumnIndex = columnEntry.sheetColumnIndex; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const value = sheetRow[sheetColumnIndex]!; + const valueType = columnEntry.valueType; + + const parsedValue = this.parseAndValidateValue(value, valueType, context); + if (parsedValue === undefined) { + const currentCellIndex = new CellIndex(sheetColumnIndex, sheetRowIndex); + context.logger.logDebug( + `Invalid value at cell ${currentCellIndex.toString()}: "${value}" does not match the type ${columnEntry.valueType.getName()}`, + ); + invalidRow = true; + return; + } + + tableRow[columnEntry.columnName] = parsedValue; + }); + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (invalidRow) { + return undefined; + } + + assert(Object.keys(tableRow).length === columnEntries.length); + return tableRow; + } +} diff --git a/libs/extensions/tabular/exec/src/lib/table-transformer-executor.spec.ts b/libs/extensions/tabular/exec/src/lib/table-transformer-executor.spec.ts new file mode 100644 index 00000000..e5d02126 --- /dev/null +++ b/libs/extensions/tabular/exec/src/lib/table-transformer-executor.spec.ts @@ -0,0 +1,236 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import path from 'node:path'; + +import * as R from '@jvalue/jayvee-execution'; +import { getTestExecutionContext } from '@jvalue/jayvee-execution/test'; +import { + type BlockDefinition, + IOType, + type JayveeServices, + createJayveeServices, +} from '@jvalue/jayvee-language-server'; +import { + type ParseHelperOptions, + expectNoParserAndLexerErrors, + loadTestExtensions, + parseHelper, + readJvTestAssetHelper, +} from '@jvalue/jayvee-language-server/test'; +import { + type AstNode, + type AstNodeLocator, + type LangiumDocument, +} from 'langium'; +import { NodeFileSystem } from 'langium/node'; + +import { + type ReducedColumnDefinitionEntry, + createTableFromLocalExcelFile, +} from '../../test/util'; + +import { TableTransformerExecutor } from './table-transformer-executor'; + +describe('Validation of TableTransformerExecutor', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + let locator: AstNodeLocator; + let services: JayveeServices; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../test/assets/table-transformer-executor/', + ); + + async function readTestExcelAllColumns() { + return readTestTable('test-excel.xlsx', [ + { + columnName: 'index', + sheetColumnIndex: 0, + valueType: services.ValueTypeProvider.Primitives.Integer, + }, + { + columnName: 'name', + sheetColumnIndex: 1, + valueType: services.ValueTypeProvider.Primitives.Text, + }, + { + columnName: 'flag', + sheetColumnIndex: 2, + valueType: services.ValueTypeProvider.Primitives.Boolean, + }, + ]); + } + async function readTestTable( + fileName: string, + columnDefinitions: ReducedColumnDefinitionEntry[], + ): Promise<R.Table> { + const absoluteFileName = path.resolve( + __dirname, + '../../test/assets/table-transformer-executor/', + fileName, + ); + return await createTableFromLocalExcelFile( + absoluteFileName, + columnDefinitions, + ); + } + + async function parseAndExecuteExecutor( + input: string, + IOInput: R.Table, + ): Promise<R.Result<R.Table>> { + const document = await parse(input, { validation: true }); + expectNoParserAndLexerErrors(document); + + const block = locator.getAstNode<BlockDefinition>( + document.parseResult.value, + 'pipelines@0/blocks@1', + ) as BlockDefinition; + + return new TableTransformerExecutor().doExecute( + IOInput, + getTestExecutionContext( + locator, + document, + services, + [block], + { + isDebugMode: false, + debugGranularity: 'minimal', + debugTargets: 'all', + }, + true, + ), + ); + } + + beforeAll(async () => { + // Create language services + services = createJayveeServices(NodeFileSystem).Jayvee; + await loadTestExtensions(services, [ + path.resolve(__dirname, '../../test/test-extension/TestBlockTypes.jv'), + ]); + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + it('should diagnose no error on valid table transform', async () => { + const text = readJvTestAsset('valid-transfomer.jv'); + + const testTable = await readTestExcelAllColumns(); + const result = await parseAndExecuteExecutor(text, testTable); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.TABLE); + expect(result.right.getNumberOfColumns()).toEqual(4); + expect(result.right.getNumberOfRows()).toEqual(6); + expect(result.right.getColumn('index')).toEqual( + expect.objectContaining({ + values: expect.arrayContaining([0, 1, 2, 5]) as number[], + }), + ); + expect(result.right.getColumn('index2')).toEqual( + expect.objectContaining({ + values: expect.arrayContaining([0, 2, 4, 10]) as number[], + }), + ); + } + }); + + it('should diagnose no error on column overwrite', async () => { + const text = readJvTestAsset('valid-column-overwrite.jv'); + + const testTable = await readTestExcelAllColumns(); + const result = await parseAndExecuteExecutor(text, testTable); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.TABLE); + expect(result.right.getNumberOfColumns()).toEqual(3); + expect(result.right.getNumberOfRows()).toEqual(6); + expect(result.right.getColumn('index')).toEqual( + expect.objectContaining({ + values: expect.arrayContaining([0, 2, 4, 10]) as number[], + }), + ); + } + }); + + it('should diagnose no error on column type change', async () => { + const text = readJvTestAsset('valid-column-type-change.jv'); + + const testTable = await readTestExcelAllColumns(); + const result = await parseAndExecuteExecutor(text, testTable); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.TABLE); + expect(result.right.getNumberOfColumns()).toEqual(3); + expect(result.right.getNumberOfRows()).toEqual(6); + expect(result.right.getColumn('index')).toEqual( + expect.objectContaining({ + values: [false, true, true, true, true, true], + valueType: services.ValueTypeProvider.Primitives.Boolean, + }), + ); + } + }); + + it('should diagnose no error on empty table', async () => { + const text = readJvTestAsset('valid-transfomer.jv'); + + const testTable = await readTestTable('test-empty.xlsx', [ + { + columnName: 'index', + sheetColumnIndex: 0, + valueType: services.ValueTypeProvider.Primitives.Integer, + }, + ]); + const result = await parseAndExecuteExecutor(text, testTable); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.TABLE); + expect(result.right.getNumberOfColumns()).toEqual(2); + expect(result.right.getNumberOfRows()).toEqual(0); + expect(result.right.getColumn('index')?.values).toHaveLength(0); + expect(result.right.getColumn('index2')?.values).toHaveLength(0); + } + }); + + it('should diagnose error on missing input column', async () => { + const text = readJvTestAsset('valid-missing-input-column.jv'); + + const testTable = await readTestExcelAllColumns(); + const result = await parseAndExecuteExecutor(text, testTable); + + expect(R.isOk(result)).toEqual(false); + if (R.isErr(result)) { + expect(result.left.message).toEqual( + 'The specified input column "id" does not exist in the given table', + ); + } + }); + + it('should diagnose error on transform type missmatch', async () => { + const text = readJvTestAsset('valid-transform-type-missmatch.jv'); + + const testTable = await readTestExcelAllColumns(); + const result = await parseAndExecuteExecutor(text, testTable); + + expect(R.isOk(result)).toEqual(false); + if (R.isErr(result)) { + expect(result.left.message).toEqual( + 'Type text of column "name" is not convertible to type integer', + ); + } + }); +}); diff --git a/libs/extensions/tabular/exec/src/lib/table-transformer-executor.ts b/libs/extensions/tabular/exec/src/lib/table-transformer-executor.ts new file mode 100644 index 00000000..cdff06fb --- /dev/null +++ b/libs/extensions/tabular/exec/src/lib/table-transformer-executor.ts @@ -0,0 +1,310 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; + +import * as R from '@jvalue/jayvee-execution'; +import { + AbstractBlockExecutor, + type BlockExecutorClass, + type ExecutionContext, + type PortDetails, + TsTransformExecutor, + implementsStatic, +} from '@jvalue/jayvee-execution'; +import { IOType } from '@jvalue/jayvee-language-server'; + +export abstract class TableTransformerExecutor extends AbstractBlockExecutor< + IOType.TABLE, + IOType.TABLE +> { + constructor() { + super(IOType.TABLE, IOType.TABLE); + } + + protected logColumnOverwriteStatus( + inputTable: R.Table, + outputColumnName: string, + context: R.ExecutionContext, + transformOutputDetails: PortDetails, + ) { + const outputColumn = inputTable.getColumn(outputColumnName); + if (outputColumn !== undefined) { + context.logger.logInfo( + `Column "${outputColumnName}" will be overwritten`, + ); + + // log if output column type changes + if ( + !outputColumn + .getValueType(context.valueTypeProvider) + .equals(transformOutputDetails.valueType) + ) { + context.logger.logInfo( + `Column "${outputColumnName}" will change its type from ${outputColumn + .getValueType(context.valueTypeProvider) + .getName()} to ${transformOutputDetails.valueType.getName()}`, + ); + } + } + } + + protected checkInputColumnsMatchTransformInputTypes( + inputColumnNames: string[], + inputTable: R.Table, + transformInputDetailsList: PortDetails[], + context: R.ExecutionContext, + ): R.Result<undefined> { + for (let i = 0; i < inputColumnNames.length; ++i) { + const inputColumnName = inputColumnNames[i]; + assert(inputColumnName !== undefined); + const inputColumn = inputTable.getColumn(inputColumnName); + assert(inputColumn !== undefined); + + const matchingInputDetails = transformInputDetailsList[i]; + assert(matchingInputDetails !== undefined); + + if ( + !inputColumn + .getValueType(context.valueTypeProvider) + .isConvertibleTo(matchingInputDetails.valueType) + ) { + return R.err({ + message: `Type ${inputColumn + .getValueType(context.valueTypeProvider) + .getName()} of column "${inputColumnName}" is not convertible to type ${matchingInputDetails.valueType.getName()}`, + diagnostic: { + node: context.getOrFailProperty('use'), + }, + }); + } + } + return R.ok(undefined); + } + + protected checkInputColumnsExist( + inputColumnNames: string[], + inputTable: R.Table, + context: R.ExecutionContext, + ): R.Result<undefined> { + // check input columns exist + let i = 0; + for (const inputColumnName of inputColumnNames) { + const inputColumn = inputTable.getColumn(inputColumnName); + if (inputColumn === undefined) { + return R.err({ + message: `The specified input column "${inputColumnName}" does not exist in the given table`, + diagnostic: { + node: context.getOrFailProperty('inputColumns').value, + property: 'values', + index: i, + }, + }); + } + ++i; + } + return R.ok(undefined); + } +} + +@implementsStatic<BlockExecutorClass>() +export class PolarsTableTransformerExecutor extends TableTransformerExecutor { + public static readonly type = 'PolarsTableTransformer'; + + // eslint-disable-next-line @typescript-eslint/require-await + override async doExecute( + inputTable: R.PolarsTable, + context: R.ExecutionContext, + ): Promise<R.Result<R.PolarsTable>> { + const inputColumnNames = context.getPropertyValue( + 'inputColumns', + context.valueTypeProvider.createCollectionValueTypeOf( + context.valueTypeProvider.Primitives.Text, + ), + ); + const outputColumnName = context.getPropertyValue( + 'outputColumn', + context.valueTypeProvider.Primitives.Text, + ); + const usedTransform = context.getPropertyValue( + 'use', + context.valueTypeProvider.Primitives.Transform, + ); + + const checkInputColumnsExistResult = this.checkInputColumnsExist( + inputColumnNames, + inputTable, + context, + ); + if (R.isErr(checkInputColumnsExistResult)) { + return checkInputColumnsExistResult; + } + + const executor = new R.PolarsTransformExecutor(usedTransform, context); + + const transformInputDetailsList = executor.getInputDetails(); + const checkInputColumnsMatchTransformInputTypesResult = + this.checkInputColumnsMatchTransformInputTypes( + inputColumnNames, + inputTable, + transformInputDetailsList, + context, + ); + if (R.isErr(checkInputColumnsMatchTransformInputTypesResult)) { + return checkInputColumnsMatchTransformInputTypesResult; + } + + const expr = executor.executeTransform(inputColumnNames, context); + + this.logColumnOverwriteStatus( + inputTable, + outputColumnName, + context, + executor.getOutputDetails(), + ); + + if (expr === undefined) { + return R.err({ + message: 'Skipping transform: Could not evaluate transform expression', + diagnostic: { + node: context.getCurrentNode(), + }, + }); + } + + const ndf = inputTable.df.withColumn(expr.alias(outputColumnName)); + return R.ok(new R.PolarsTable(ndf)); + } +} + +@implementsStatic<BlockExecutorClass>() +export class TsTableTransformerExecutor extends TableTransformerExecutor { + public static readonly type = 'TsTableTransformer'; + + // eslint-disable-next-line @typescript-eslint/require-await + override async doExecute( + inputTable: R.TsTable, + context: ExecutionContext, + ): Promise<R.Result<R.TsTable>> { + const inputColumnNames = context.getPropertyValue( + 'inputColumns', + context.valueTypeProvider.createCollectionValueTypeOf( + context.valueTypeProvider.Primitives.Text, + ), + ); + const outputColumnName = context.getPropertyValue( + 'outputColumn', + context.valueTypeProvider.Primitives.Text, + ); + const usedTransform = context.getPropertyValue( + 'use', + context.valueTypeProvider.Primitives.Transform, + ); + + const checkInputColumnsExistResult = this.checkInputColumnsExist( + inputColumnNames, + inputTable, + context, + ); + if (R.isErr(checkInputColumnsExistResult)) { + return checkInputColumnsExistResult; + } + + const executor = new TsTransformExecutor(usedTransform, context); + const transformInputDetailsList = executor.getInputDetails(); + const transformOutputDetails = executor.getOutputDetails(); + + const checkInputColumnsMatchTransformInputTypesResult = + this.checkInputColumnsMatchTransformInputTypes( + inputColumnNames, + inputTable, + transformInputDetailsList, + context, + ); + if (R.isErr(checkInputColumnsMatchTransformInputTypesResult)) { + return checkInputColumnsMatchTransformInputTypesResult; + } + const variableToColumnMap: Map<string, R.TsTableColumn> = new Map(); + inputColumnNames.forEach((inputColumnName) => { + const col = inputTable.getColumn(inputColumnName); + assert(col !== undefined); + variableToColumnMap.set(inputColumnName, col); + }); + + this.logColumnOverwriteStatus( + inputTable, + outputColumnName, + context, + transformOutputDetails, + ); + + const transformResult = executor.executeTransform( + { + columns: variableToColumnMap, + numberOfRows: inputTable.getNumberOfRows(), + }, + context, + ); + + if (transformResult === undefined) { + return R.ok(inputTable); + } + + const outputTable = this.createOutputTable( + inputTable, + transformResult, + outputColumnName, + ); + + return R.ok(outputTable); + } + + private createOutputTable( + inputTable: R.TsTable, + transformResult: { + resultingColumn: R.TsTableColumn; + rowsToDelete: number[]; + }, + outputColumnName: string, + ) { + const outputTable = inputTable.clone(); + outputTable.dropRows(transformResult.rowsToDelete); + outputTable.addColumn(outputColumnName, transformResult.resultingColumn); + return outputTable; + } + + protected override checkInputColumnsMatchTransformInputTypes( + inputColumnNames: string[], + inputTable: R.Table, + transformInputDetailsList: PortDetails[], + context: R.ExecutionContext, + ): R.Result<undefined> { + for (let i = 0; i < inputColumnNames.length; ++i) { + const inputColumnName = inputColumnNames[i]; + assert(inputColumnName !== undefined); + const inputColumn = inputTable.getColumn(inputColumnName); + assert(inputColumn !== undefined); + + const matchingInputDetails = transformInputDetailsList[i]; + assert(matchingInputDetails !== undefined); + + if ( + !inputColumn + .getValueType(context.valueTypeProvider) + .isConvertibleTo(matchingInputDetails.valueType) + ) { + return R.err({ + message: `Type ${inputColumn + .getValueType(context.valueTypeProvider) + .getName()} of column "${inputColumnName}" is not convertible to type ${matchingInputDetails.valueType.getName()}`, + diagnostic: { + node: context.getOrFailProperty('use'), + }, + }); + } + } + return R.ok(undefined); + } +} diff --git a/libs/extensions/tabular/exec/src/lib/xlsx-interpreter-executor.spec.ts b/libs/extensions/tabular/exec/src/lib/xlsx-interpreter-executor.spec.ts new file mode 100644 index 00000000..5ce21e68 --- /dev/null +++ b/libs/extensions/tabular/exec/src/lib/xlsx-interpreter-executor.spec.ts @@ -0,0 +1,141 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import path from 'node:path'; + +import * as R from '@jvalue/jayvee-execution'; +import { + createBinaryFileFromLocalFile, + getTestExecutionContext, +} from '@jvalue/jayvee-execution/test'; +import { + type BlockDefinition, + IOType, + type JayveeServices, + createJayveeServices, +} from '@jvalue/jayvee-language-server'; +import { + type ParseHelperOptions, + expectNoParserAndLexerErrors, + loadTestExtensions, + parseHelper, + readJvTestAssetHelper, +} from '@jvalue/jayvee-language-server/test'; +import { + type AstNode, + type AstNodeLocator, + type LangiumDocument, +} from 'langium'; +import { NodeFileSystem } from 'langium/node'; + +import { XLSXInterpreterExecutor } from './xlsx-interpreter-executor'; + +describe('Validation of XLSXInterpreterExecutor', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + let locator: AstNodeLocator; + let services: JayveeServices; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../test/assets/xlsx-interpreter-executor/', + ); + + function readTestFile(fileName: string): R.BinaryFile { + const absoluteFileName = path.resolve( + __dirname, + '../../test/assets/xlsx-interpreter-executor/', + fileName, + ); + return createBinaryFileFromLocalFile(absoluteFileName); + } + + function expectSheetRowAndColumnSize( + sheet: R.Sheet | undefined, + cols: number, + rows: number, + ) { + expect(sheet?.getNumberOfColumns()).toEqual(cols); + expect(sheet?.getNumberOfRows()).toEqual(rows); + } + + async function parseAndExecuteExecutor( + input: string, + IOInput: R.BinaryFile, + ): Promise<R.Result<R.Workbook>> { + const document = await parse(input, { validation: true }); + expectNoParserAndLexerErrors(document); + + const block = locator.getAstNode<BlockDefinition>( + document.parseResult.value, + 'pipelines@0/blocks@1', + ) as BlockDefinition; + + return new XLSXInterpreterExecutor().doExecute( + IOInput, + getTestExecutionContext(locator, document, services, [block]), + ); + } + + beforeAll(async () => { + // Create language services + services = createJayveeServices(NodeFileSystem).Jayvee; + await loadTestExtensions(services, [ + path.resolve(__dirname, '../../test/test-extension/TestBlockTypes.jv'), + ]); + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + it('should diagnose no error on single sheet excel', async () => { + const text = readJvTestAsset('valid-excel-interpreter.jv'); + + const testFile = readTestFile('test-excel.xlsx'); + const result = await parseAndExecuteExecutor(text, testFile); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.WORKBOOK); + const sheets = result.right.getSheets(); + expect(sheets.size).toEqual(1); + expectSheetRowAndColumnSize(sheets.get('Sheet1'), 3, 6); + } + }); + + it('should diagnose no error on multiple sheet excel', async () => { + const text = readJvTestAsset('valid-excel-interpreter.jv'); + + const testFile = readTestFile('test-multiple-sheets.xlsx'); + const result = await parseAndExecuteExecutor(text, testFile); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.WORKBOOK); + const sheets = result.right.getSheets(); + expect(sheets.size).toEqual(3); + expectSheetRowAndColumnSize(sheets.get('Sheet1'), 3, 6); + expectSheetRowAndColumnSize(sheets.get('CustomSheet'), 0, 0); + expectSheetRowAndColumnSize(sheets.get('Sheet3'), 0, 0); + } + }); + + it('should diagnose no error on empty excel', async () => { + const text = readJvTestAsset('valid-excel-interpreter.jv'); + + const testFile = readTestFile('test-empty.xlsx'); + const result = await parseAndExecuteExecutor(text, testFile); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.WORKBOOK); + const sheets = result.right.getSheets(); + expect(sheets.size).toEqual(1); + expectSheetRowAndColumnSize(sheets.get('Sheet1'), 0, 0); + } + }); +}); diff --git a/libs/extensions/tabular/exec/src/lib/xlsx-interpreter-executor.ts b/libs/extensions/tabular/exec/src/lib/xlsx-interpreter-executor.ts new file mode 100644 index 00000000..0c7fbdfc --- /dev/null +++ b/libs/extensions/tabular/exec/src/lib/xlsx-interpreter-executor.ts @@ -0,0 +1,60 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import * as R from '@jvalue/jayvee-execution'; +import { + AbstractBlockExecutor, + type BinaryFile, + type BlockExecutorClass, + type ExecutionContext, + Workbook, + implementsStatic, +} from '@jvalue/jayvee-execution'; +import { IOType } from '@jvalue/jayvee-language-server'; +import * as exceljs from 'exceljs'; + +@implementsStatic<BlockExecutorClass>() +export class XLSXInterpreterExecutor extends AbstractBlockExecutor< + IOType.FILE, + IOType.WORKBOOK +> { + public static readonly type = 'XLSXInterpreter'; + + constructor() { + super(IOType.FILE, IOType.WORKBOOK); + } + + async doExecute( + file: BinaryFile, + context: ExecutionContext, + ): Promise<R.Result<Workbook>> { + context.logger.logDebug(`Reading from XLSX file`); + const workBookFromFile = new exceljs.Workbook(); + await workBookFromFile.xlsx.load(file.content); + + const workbook = new Workbook(); + + workBookFromFile.eachSheet((workSheet) => { + const workSheetDataArray: string[][] = []; + workSheet.eachRow((row, rowNumber) => { + const cellValues: string[] = []; + + // ExcelJS Rows and Columns are indexed from 1 + // We reduce their index to match Sheets being zero indexed + row.eachCell( + { includeEmpty: true }, + (cell: exceljs.Cell, colNumber: number) => { + cellValues[colNumber - 1] = cell.text; + }, + ); + + workSheetDataArray[rowNumber - 1] = cellValues; + }); + + workbook.addSheet(workSheetDataArray, workSheet.name); + }); + + return Promise.resolve(R.ok(workbook)); + } +} diff --git a/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/test-A1-C16.xlsx b/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/test-A1-C16.xlsx new file mode 100644 index 00000000..0544fa28 Binary files /dev/null and b/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/test-A1-C16.xlsx differ diff --git a/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/test-A1-C16.xlsx.license b/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/test-A1-C16.xlsx.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/test-A1-C16.xlsx.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/test-B1-C2.xlsx b/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/test-B1-C2.xlsx new file mode 100644 index 00000000..fdc4858c Binary files /dev/null and b/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/test-B1-C2.xlsx differ diff --git a/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/test-B1-C2.xlsx.license b/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/test-B1-C2.xlsx.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/test-B1-C2.xlsx.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/test-empty.xlsx b/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/test-empty.xlsx new file mode 100644 index 00000000..af7b184b Binary files /dev/null and b/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/test-empty.xlsx differ diff --git a/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/test-empty.xlsx.license b/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/test-empty.xlsx.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/test-empty.xlsx.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/valid-A1-A4.jv b/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/valid-A1-A4.jv new file mode 100644 index 00000000..4fa94a1c --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/valid-A1-A4.jv @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestSheetExtractor { + } + + block TestBlock oftype CellRangeSelector { + select: range A1:A4; + } + + block TestLoader oftype TestSheetLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/valid-A1-C.jv b/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/valid-A1-C.jv new file mode 100644 index 00000000..0ed56a14 --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/valid-A1-C.jv @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestSheetExtractor { + } + + block TestBlock oftype CellRangeSelector { + select: range A1:C*; + } + + block TestLoader oftype TestSheetLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/valid-A1-E4.jv b/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/valid-A1-E4.jv new file mode 100644 index 00000000..e004eb92 --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/valid-A1-E4.jv @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestSheetExtractor { + } + + block TestBlock oftype CellRangeSelector { + select: range A1:E4; + } + + block TestLoader oftype TestSheetLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/tabular/exec/test/assets/cell-writer-executor/test-A1-C16.xlsx b/libs/extensions/tabular/exec/test/assets/cell-writer-executor/test-A1-C16.xlsx new file mode 100644 index 00000000..0544fa28 Binary files /dev/null and b/libs/extensions/tabular/exec/test/assets/cell-writer-executor/test-A1-C16.xlsx differ diff --git a/libs/extensions/tabular/exec/test/assets/cell-writer-executor/test-A1-C16.xlsx.license b/libs/extensions/tabular/exec/test/assets/cell-writer-executor/test-A1-C16.xlsx.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/cell-writer-executor/test-A1-C16.xlsx.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/tabular/exec/test/assets/cell-writer-executor/test-empty.xlsx b/libs/extensions/tabular/exec/test/assets/cell-writer-executor/test-empty.xlsx new file mode 100644 index 00000000..af7b184b Binary files /dev/null and b/libs/extensions/tabular/exec/test/assets/cell-writer-executor/test-empty.xlsx differ diff --git a/libs/extensions/tabular/exec/test/assets/cell-writer-executor/test-empty.xlsx.license b/libs/extensions/tabular/exec/test/assets/cell-writer-executor/test-empty.xlsx.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/cell-writer-executor/test-empty.xlsx.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/tabular/exec/test/assets/cell-writer-executor/valid-cell-range-writer.jv b/libs/extensions/tabular/exec/test/assets/cell-writer-executor/valid-cell-range-writer.jv new file mode 100644 index 00000000..327d9dba --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/cell-writer-executor/valid-cell-range-writer.jv @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestSheetExtractor { + } + + block TestBlock oftype CellWriter { + write: ["16", "Test2", ""]; + at: range A1:C1; + } + + block TestLoader oftype TestSheetLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/tabular/exec/test/assets/cell-writer-executor/valid-single-cell-writer.jv b/libs/extensions/tabular/exec/test/assets/cell-writer-executor/valid-single-cell-writer.jv new file mode 100644 index 00000000..844a8fd8 --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/cell-writer-executor/valid-single-cell-writer.jv @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestSheetExtractor { + } + + block TestBlock oftype CellWriter { + write: ["16"]; + at: cell A1; + } + + block TestLoader oftype TestSheetLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/tabular/exec/test/assets/column-deleter-executor/test-A1-B2.xlsx b/libs/extensions/tabular/exec/test/assets/column-deleter-executor/test-A1-B2.xlsx new file mode 100644 index 00000000..3465ea34 Binary files /dev/null and b/libs/extensions/tabular/exec/test/assets/column-deleter-executor/test-A1-B2.xlsx differ diff --git a/libs/extensions/tabular/exec/test/assets/column-deleter-executor/test-A1-B2.xlsx.license b/libs/extensions/tabular/exec/test/assets/column-deleter-executor/test-A1-B2.xlsx.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/column-deleter-executor/test-A1-B2.xlsx.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/tabular/exec/test/assets/column-deleter-executor/test-A1-C16.xlsx b/libs/extensions/tabular/exec/test/assets/column-deleter-executor/test-A1-C16.xlsx new file mode 100644 index 00000000..0544fa28 Binary files /dev/null and b/libs/extensions/tabular/exec/test/assets/column-deleter-executor/test-A1-C16.xlsx differ diff --git a/libs/extensions/tabular/exec/test/assets/column-deleter-executor/test-A1-C16.xlsx.license b/libs/extensions/tabular/exec/test/assets/column-deleter-executor/test-A1-C16.xlsx.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/column-deleter-executor/test-A1-C16.xlsx.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/tabular/exec/test/assets/column-deleter-executor/valid-duplicate-column.jv b/libs/extensions/tabular/exec/test/assets/column-deleter-executor/valid-duplicate-column.jv new file mode 100644 index 00000000..b3b6a6ae --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/column-deleter-executor/valid-duplicate-column.jv @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestSheetExtractor { + } + + block TestBlock oftype ColumnDeleter { + delete: [column A, column A]; + } + + block TestLoader oftype TestSheetLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/tabular/exec/test/assets/column-deleter-executor/valid-multiple-column-deleter.jv b/libs/extensions/tabular/exec/test/assets/column-deleter-executor/valid-multiple-column-deleter.jv new file mode 100644 index 00000000..6a2aa56d --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/column-deleter-executor/valid-multiple-column-deleter.jv @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestSheetExtractor { + } + + block TestBlock oftype ColumnDeleter { + delete: [column A, column C]; + } + + block TestLoader oftype TestSheetLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/tabular/exec/test/assets/column-deleter-executor/valid-single-column-deleter.jv b/libs/extensions/tabular/exec/test/assets/column-deleter-executor/valid-single-column-deleter.jv new file mode 100644 index 00000000..ad5f6733 --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/column-deleter-executor/valid-single-column-deleter.jv @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestSheetExtractor { + } + + block TestBlock oftype ColumnDeleter { + delete: [column A]; + } + + block TestLoader oftype TestSheetLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/tabular/exec/test/assets/csv-interpreter-executor/valid-csv-interpreter.jv b/libs/extensions/tabular/exec/test/assets/csv-interpreter-executor/valid-csv-interpreter.jv new file mode 100644 index 00000000..856c67eb --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/csv-interpreter-executor/valid-csv-interpreter.jv @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestTextFileExtractor { + } + + block TestBlock oftype CSVInterpreter { + } + + block TestLoader oftype TestSheetLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/tabular/exec/test/assets/csv-interpreter-executor/valid-csv.csv b/libs/extensions/tabular/exec/test/assets/csv-interpreter-executor/valid-csv.csv new file mode 100644 index 00000000..086eb44a --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/csv-interpreter-executor/valid-csv.csv @@ -0,0 +1,2 @@ +Test,true +Test,false diff --git a/libs/extensions/tabular/exec/test/assets/csv-interpreter-executor/valid-csv.csv.license b/libs/extensions/tabular/exec/test/assets/csv-interpreter-executor/valid-csv.csv.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/csv-interpreter-executor/valid-csv.csv.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/tabular/exec/test/assets/row-deleter-executor/test-A1-B2.xlsx b/libs/extensions/tabular/exec/test/assets/row-deleter-executor/test-A1-B2.xlsx new file mode 100644 index 00000000..3465ea34 Binary files /dev/null and b/libs/extensions/tabular/exec/test/assets/row-deleter-executor/test-A1-B2.xlsx differ diff --git a/libs/extensions/tabular/exec/test/assets/row-deleter-executor/test-A1-B2.xlsx.license b/libs/extensions/tabular/exec/test/assets/row-deleter-executor/test-A1-B2.xlsx.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/row-deleter-executor/test-A1-B2.xlsx.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/tabular/exec/test/assets/row-deleter-executor/test-A1-C16.xlsx b/libs/extensions/tabular/exec/test/assets/row-deleter-executor/test-A1-C16.xlsx new file mode 100644 index 00000000..0544fa28 Binary files /dev/null and b/libs/extensions/tabular/exec/test/assets/row-deleter-executor/test-A1-C16.xlsx differ diff --git a/libs/extensions/tabular/exec/test/assets/row-deleter-executor/test-A1-C16.xlsx.license b/libs/extensions/tabular/exec/test/assets/row-deleter-executor/test-A1-C16.xlsx.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/row-deleter-executor/test-A1-C16.xlsx.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/tabular/exec/test/assets/row-deleter-executor/valid-duplicate-row.jv b/libs/extensions/tabular/exec/test/assets/row-deleter-executor/valid-duplicate-row.jv new file mode 100644 index 00000000..cbb08f0f --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/row-deleter-executor/valid-duplicate-row.jv @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestSheetExtractor { + } + + block TestBlock oftype RowDeleter { + delete: [row 1, row 1]; + } + + block TestLoader oftype TestSheetLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/tabular/exec/test/assets/row-deleter-executor/valid-multiple-row-deleter.jv b/libs/extensions/tabular/exec/test/assets/row-deleter-executor/valid-multiple-row-deleter.jv new file mode 100644 index 00000000..d1384e50 --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/row-deleter-executor/valid-multiple-row-deleter.jv @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestSheetExtractor { + } + + block TestBlock oftype RowDeleter { + delete: [row 1, row 5]; + } + + block TestLoader oftype TestSheetLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/tabular/exec/test/assets/row-deleter-executor/valid-single-row-deleter.jv b/libs/extensions/tabular/exec/test/assets/row-deleter-executor/valid-single-row-deleter.jv new file mode 100644 index 00000000..cbb6610f --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/row-deleter-executor/valid-single-row-deleter.jv @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestSheetExtractor { + } + + block TestBlock oftype RowDeleter { + delete: [row 1]; + } + + block TestLoader oftype TestSheetLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/tabular/exec/test/assets/sheet-picker-executor/test-A1-C16.xlsx b/libs/extensions/tabular/exec/test/assets/sheet-picker-executor/test-A1-C16.xlsx new file mode 100644 index 00000000..0544fa28 Binary files /dev/null and b/libs/extensions/tabular/exec/test/assets/sheet-picker-executor/test-A1-C16.xlsx differ diff --git a/libs/extensions/tabular/exec/test/assets/sheet-picker-executor/test-A1-C16.xlsx.license b/libs/extensions/tabular/exec/test/assets/sheet-picker-executor/test-A1-C16.xlsx.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/sheet-picker-executor/test-A1-C16.xlsx.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/tabular/exec/test/assets/sheet-picker-executor/valid-custom-sheet-name.jv b/libs/extensions/tabular/exec/test/assets/sheet-picker-executor/valid-custom-sheet-name.jv new file mode 100644 index 00000000..11b75317 --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/sheet-picker-executor/valid-custom-sheet-name.jv @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestWorkbookExtractor { + } + + block TestBlock oftype SheetPicker { + sheetName: "MyCustomSheet"; + } + + block TestLoader oftype TestSheetLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/tabular/exec/test/assets/sheet-picker-executor/valid-sheet-picker.jv b/libs/extensions/tabular/exec/test/assets/sheet-picker-executor/valid-sheet-picker.jv new file mode 100644 index 00000000..40654c11 --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/sheet-picker-executor/valid-sheet-picker.jv @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestWorkbookExtractor { + } + + block TestBlock oftype SheetPicker { + sheetName: "Sheet1"; + } + + block TestLoader oftype TestSheetLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/test-empty.xlsx b/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/test-empty.xlsx new file mode 100644 index 00000000..af7b184b Binary files /dev/null and b/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/test-empty.xlsx differ diff --git a/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/test-empty.xlsx.license b/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/test-empty.xlsx.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/test-empty.xlsx.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/test-with-header.xlsx b/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/test-with-header.xlsx new file mode 100644 index 00000000..9417e87a Binary files /dev/null and b/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/test-with-header.xlsx differ diff --git a/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/test-with-header.xlsx.license b/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/test-with-header.xlsx.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/test-with-header.xlsx.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/test-without-header.xlsx b/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/test-without-header.xlsx new file mode 100644 index 00000000..88085963 Binary files /dev/null and b/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/test-without-header.xlsx differ diff --git a/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/test-without-header.xlsx.license b/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/test-without-header.xlsx.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/test-without-header.xlsx.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/valid-empty-columns-with-header.jv b/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/valid-empty-columns-with-header.jv new file mode 100644 index 00000000..7e46a44c --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/valid-empty-columns-with-header.jv @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestSheetExtractor { + } + + block TestBlock oftype TableInterpreter { + header: true; + columns: []; + } + + block TestLoader oftype TestTableLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/valid-empty-columns-without-header.jv b/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/valid-empty-columns-without-header.jv new file mode 100644 index 00000000..b6e423ed --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/valid-empty-columns-without-header.jv @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestSheetExtractor { + } + + block TestBlock oftype TableInterpreter { + header: false; + columns: []; + } + + block TestLoader oftype TestTableLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/valid-with-capitalized-header.jv b/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/valid-with-capitalized-header.jv new file mode 100644 index 00000000..3d992949 --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/valid-with-capitalized-header.jv @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestSheetExtractor { + } + + block TestBlock oftype TableInterpreter { + header: true; + columns: [ + "Index" oftype integer, + "Name" oftype text, + "Flag" oftype boolean + ]; + } + + block TestLoader oftype TestTableLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/valid-with-header.jv b/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/valid-with-header.jv new file mode 100644 index 00000000..cd368186 --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/valid-with-header.jv @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestSheetExtractor { + } + + block TestBlock oftype TableInterpreter { + header: true; + columns: [ + "index" oftype integer, + "name" oftype text, + "flag" oftype boolean + ]; + } + + block TestLoader oftype TestTableLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/valid-without-header.jv b/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/valid-without-header.jv new file mode 100644 index 00000000..6fa8f5c1 --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/valid-without-header.jv @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestSheetExtractor { + } + + block TestBlock oftype TableInterpreter { + header: false; + columns: [ + "index" oftype integer, + "name" oftype text, + "flag" oftype boolean + ]; + } + + block TestLoader oftype TestTableLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/valid-wrong-value-type-with-header.jv b/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/valid-wrong-value-type-with-header.jv new file mode 100644 index 00000000..f8ec0344 --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/valid-wrong-value-type-with-header.jv @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestSheetExtractor { + } + + block TestBlock oftype TableInterpreter { + header: true; + columns: [ + "index" oftype integer, + "name" oftype text, + "flag" oftype integer + ]; + } + + block TestLoader oftype TestTableLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/valid-wrong-value-type-without-header.jv b/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/valid-wrong-value-type-without-header.jv new file mode 100644 index 00000000..ebc26917 --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/valid-wrong-value-type-without-header.jv @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestSheetExtractor { + } + + block TestBlock oftype TableInterpreter { + header: false; + columns: [ + "index" oftype integer, + "name" oftype text, + "flag" oftype integer + ]; + } + + block TestLoader oftype TestTableLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/tabular/exec/test/assets/table-transformer-executor/test-empty.xlsx b/libs/extensions/tabular/exec/test/assets/table-transformer-executor/test-empty.xlsx new file mode 100644 index 00000000..af7b184b Binary files /dev/null and b/libs/extensions/tabular/exec/test/assets/table-transformer-executor/test-empty.xlsx differ diff --git a/libs/extensions/tabular/exec/test/assets/table-transformer-executor/test-empty.xlsx.license b/libs/extensions/tabular/exec/test/assets/table-transformer-executor/test-empty.xlsx.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/table-transformer-executor/test-empty.xlsx.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/tabular/exec/test/assets/table-transformer-executor/test-excel.xlsx b/libs/extensions/tabular/exec/test/assets/table-transformer-executor/test-excel.xlsx new file mode 100644 index 00000000..8869be3b Binary files /dev/null and b/libs/extensions/tabular/exec/test/assets/table-transformer-executor/test-excel.xlsx differ diff --git a/libs/extensions/tabular/exec/test/assets/table-transformer-executor/test-excel.xlsx.license b/libs/extensions/tabular/exec/test/assets/table-transformer-executor/test-excel.xlsx.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/table-transformer-executor/test-excel.xlsx.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/tabular/exec/test/assets/table-transformer-executor/valid-column-overwrite.jv b/libs/extensions/tabular/exec/test/assets/table-transformer-executor/valid-column-overwrite.jv new file mode 100644 index 00000000..91c301ed --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/table-transformer-executor/valid-column-overwrite.jv @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + transform Double { + from original oftype integer; + to double oftype integer; + + double: original * 2; + } + + block TestExtractor oftype TestTableExtractor { + } + + block TestBlock oftype TableTransformer { + inputColumns: ['index']; + outputColumn: 'index'; + use: Double; + } + + block TestLoader oftype TestTableLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/tabular/exec/test/assets/table-transformer-executor/valid-column-type-change.jv b/libs/extensions/tabular/exec/test/assets/table-transformer-executor/valid-column-type-change.jv new file mode 100644 index 00000000..d17c866b --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/table-transformer-executor/valid-column-type-change.jv @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + transform ToBool { + from integer oftype integer; + to boolean oftype boolean; + + boolean: integer != 0; + } + + block TestExtractor oftype TestTableExtractor { + } + + block TestBlock oftype TableTransformer { + inputColumns: ['index']; + outputColumn: 'index'; + use: ToBool; + } + + block TestLoader oftype TestTableLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/tabular/exec/test/assets/table-transformer-executor/valid-missing-input-column.jv b/libs/extensions/tabular/exec/test/assets/table-transformer-executor/valid-missing-input-column.jv new file mode 100644 index 00000000..44e535ff --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/table-transformer-executor/valid-missing-input-column.jv @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + transform Add { + from left oftype integer; + from right oftype integer; + to added oftype integer; + + added: left + right; + } + + block TestExtractor oftype TestTableExtractor { + } + + block TestBlock oftype TableTransformer { + inputColumns: ['index', 'id']; + outputColumn: 'add'; + use: Add; + } + + block TestLoader oftype TestTableLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/tabular/exec/test/assets/table-transformer-executor/valid-transfomer.jv b/libs/extensions/tabular/exec/test/assets/table-transformer-executor/valid-transfomer.jv new file mode 100644 index 00000000..856d8024 --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/table-transformer-executor/valid-transfomer.jv @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + transform Double { + from original oftype integer; + to double oftype integer; + + double: original * 2; + } + + block TestExtractor oftype TestTableExtractor { + } + + block TestBlock oftype TableTransformer { + inputColumns: ['index']; + outputColumn: 'index2'; + use: Double; + } + + block TestLoader oftype TestTableLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/tabular/exec/test/assets/table-transformer-executor/valid-transform-type-missmatch.jv b/libs/extensions/tabular/exec/test/assets/table-transformer-executor/valid-transform-type-missmatch.jv new file mode 100644 index 00000000..52675e73 --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/table-transformer-executor/valid-transform-type-missmatch.jv @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + transform Double { + from original oftype integer; + to double oftype integer; + + double: original * 2; + } + + block TestExtractor oftype TestTableExtractor { + } + + block TestBlock oftype TableTransformer { + inputColumns: ['name']; + outputColumn: 'index2'; + use: Double; + } + + block TestLoader oftype TestTableLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/tabular/exec/test/assets/xlsx-interpreter-executor/test-empty.xlsx b/libs/extensions/tabular/exec/test/assets/xlsx-interpreter-executor/test-empty.xlsx new file mode 100644 index 00000000..af7b184b Binary files /dev/null and b/libs/extensions/tabular/exec/test/assets/xlsx-interpreter-executor/test-empty.xlsx differ diff --git a/libs/extensions/tabular/exec/test/assets/xlsx-interpreter-executor/test-empty.xlsx.license b/libs/extensions/tabular/exec/test/assets/xlsx-interpreter-executor/test-empty.xlsx.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/xlsx-interpreter-executor/test-empty.xlsx.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/tabular/exec/test/assets/xlsx-interpreter-executor/test-excel.xlsx b/libs/extensions/tabular/exec/test/assets/xlsx-interpreter-executor/test-excel.xlsx new file mode 100644 index 00000000..8869be3b Binary files /dev/null and b/libs/extensions/tabular/exec/test/assets/xlsx-interpreter-executor/test-excel.xlsx differ diff --git a/libs/extensions/tabular/exec/test/assets/xlsx-interpreter-executor/test-excel.xlsx.license b/libs/extensions/tabular/exec/test/assets/xlsx-interpreter-executor/test-excel.xlsx.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/xlsx-interpreter-executor/test-excel.xlsx.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/tabular/exec/test/assets/xlsx-interpreter-executor/test-multiple-sheets.xlsx b/libs/extensions/tabular/exec/test/assets/xlsx-interpreter-executor/test-multiple-sheets.xlsx new file mode 100644 index 00000000..3f3619fc Binary files /dev/null and b/libs/extensions/tabular/exec/test/assets/xlsx-interpreter-executor/test-multiple-sheets.xlsx differ diff --git a/libs/extensions/tabular/exec/test/assets/xlsx-interpreter-executor/test-multiple-sheets.xlsx.license b/libs/extensions/tabular/exec/test/assets/xlsx-interpreter-executor/test-multiple-sheets.xlsx.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/xlsx-interpreter-executor/test-multiple-sheets.xlsx.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/tabular/exec/test/assets/xlsx-interpreter-executor/valid-excel-interpreter.jv b/libs/extensions/tabular/exec/test/assets/xlsx-interpreter-executor/valid-excel-interpreter.jv new file mode 100644 index 00000000..a267981e --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/xlsx-interpreter-executor/valid-excel-interpreter.jv @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + block TestExtractor oftype TestFileExtractor { + } + + block TestBlock oftype XLSXInterpreter { + } + + block TestLoader oftype TestWorkbookLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/tabular/exec/test/test-extension/TestBlockTypes.jv b/libs/extensions/tabular/exec/test/test-extension/TestBlockTypes.jv new file mode 100644 index 00000000..ba08556c --- /dev/null +++ b/libs/extensions/tabular/exec/test/test-extension/TestBlockTypes.jv @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +builtin blocktype TestSheetExtractor { + input inPort oftype None; + output outPort oftype Sheet; +} + +builtin blocktype TestSheetLoader { + input inPort oftype Sheet; + output outPort oftype None; +} + +builtin blocktype TestTextFileExtractor { + input inPort oftype None; + output outPort oftype TextFile; +} + +builtin blocktype TestFileExtractor { + input inPort oftype None; + output outPort oftype File; +} + +builtin blocktype TestWorkbookExtractor { + input inPort oftype None; + output outPort oftype Workbook; +} + +builtin blocktype TestWorkbookLoader { + input inPort oftype Workbook; + output outPort oftype None; +} + +builtin blocktype TestTableExtractor { + input inPort oftype None; + output outPort oftype Table; +} + +builtin blocktype TestTableLoader { + input inPort oftype Table; + output outPort oftype None; +} diff --git a/libs/extensions/tabular/exec/test/util.ts b/libs/extensions/tabular/exec/test/util.ts new file mode 100644 index 00000000..8e536d23 --- /dev/null +++ b/libs/extensions/tabular/exec/test/util.ts @@ -0,0 +1,124 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import path from 'node:path'; + +import { + Table, + type TableRow, + Workbook, + parseValueToInternalRepresentation, +} from '@jvalue/jayvee-execution'; +import { + type InternalValueRepresentation, + type ValueType, +} from '@jvalue/jayvee-language-server'; +import * as exceljs from 'exceljs'; + +import { type ColumnDefinitionEntry } from '../src/lib/table-interpreter-executor'; + +export async function createWorkbookFromLocalExcelFile( + fileName: string, +): Promise<Workbook> { + const workBookFromFile = new exceljs.Workbook(); + await workBookFromFile.xlsx.readFile(path.resolve(__dirname, fileName)); + + const workbook = new Workbook(); + + workBookFromFile.eachSheet((workSheet) => { + const workSheetDataArray: string[][] = []; + workSheet.eachRow((row, rowNumber) => { + const cellValues: string[] = []; + + // ExcelJS Rows and Columns are indexed from 1 + // We reduce their index to match Sheets being zero indexed + row.eachCell( + { includeEmpty: true }, + (cell: exceljs.Cell, colNumber: number) => { + cellValues[colNumber - 1] = cell.text; + }, + ); + + workSheetDataArray[rowNumber - 1] = cellValues; + }); + + workbook.addSheet(workSheetDataArray, workSheet.name); + }); + + return workbook; +} + +export type ReducedColumnDefinitionEntry = Pick< + ColumnDefinitionEntry, + 'sheetColumnIndex' | 'columnName' | 'valueType' +>; + +/** + * Creates a Table from the first sheet of the excel file pointed to by {@link fileName} + * and parsed using the given column definitions. + * Note: The parsing is static and thus cannot detect runtime types! + * @param fileName + * @param columnDefinitions columns to be read from table (no header matching) + * @returns Table containing data of excel + */ +export async function createTableFromLocalExcelFile( + fileName: string, + columnDefinitions: ReducedColumnDefinitionEntry[], +): Promise<Table> { + const workBookFromFile = new exceljs.Workbook(); + await workBookFromFile.xlsx.readFile(path.resolve(__dirname, fileName)); + + const workSheet = workBookFromFile.worksheets[0] as exceljs.Worksheet; + const table = new Table(); + + columnDefinitions.forEach((columnDefinition) => { + table.addColumn(columnDefinition.columnName, { + values: [], + valueType: columnDefinition.valueType, + }); + }); + + workSheet.eachRow((row) => { + const tableRow = constructTableRow(row, columnDefinitions); + table.addRow(tableRow); + }); + + return table; +} +function constructTableRow( + row: exceljs.Row, + columnDefinitions: ReducedColumnDefinitionEntry[], +): TableRow { + const tableRow: TableRow = {}; + + row.eachCell( + { includeEmpty: true }, + (cell: exceljs.Cell, colNumber: number) => { + if (colNumber > columnDefinitions.length) return; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const columnDefinition = columnDefinitions[colNumber - 1]!; + const value = cell.text; + const valueType = columnDefinition.valueType; + + const parsedValue = parseAndValidatePrimitiveValue(value, valueType); + if (parsedValue === undefined) { + return; + } + + tableRow[columnDefinition.columnName] = parsedValue; + }, + ); + return tableRow; +} +function parseAndValidatePrimitiveValue( + value: string, + valueType: ValueType, +): InternalValueRepresentation | undefined { + const parsedValue = parseValueToInternalRepresentation(value, valueType); + if (parsedValue === undefined) { + return undefined; + } + + return parsedValue; +} diff --git a/libs/extensions/tabular/exec/tsconfig.json b/libs/extensions/tabular/exec/tsconfig.json new file mode 100644 index 00000000..d2cccb9f --- /dev/null +++ b/libs/extensions/tabular/exec/tsconfig.json @@ -0,0 +1,21 @@ +{ + "extends": "../../../../tsconfig.base.json", + "compilerOptions": { + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/libs/extensions/tabular/exec/tsconfig.json.license b/libs/extensions/tabular/exec/tsconfig.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/tabular/exec/tsconfig.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/tabular/exec/tsconfig.lib.json b/libs/extensions/tabular/exec/tsconfig.lib.json new file mode 100644 index 00000000..827c0721 --- /dev/null +++ b/libs/extensions/tabular/exec/tsconfig.lib.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../../dist/out-tsc", + "declaration": true, + "types": ["node"] + }, + "include": ["src/**/*.ts"], + "exclude": ["vite.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] +} diff --git a/libs/extensions/tabular/exec/tsconfig.lib.json.license b/libs/extensions/tabular/exec/tsconfig.lib.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/tabular/exec/tsconfig.lib.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/tabular/exec/tsconfig.spec.json b/libs/extensions/tabular/exec/tsconfig.spec.json new file mode 100644 index 00000000..71aaed79 --- /dev/null +++ b/libs/extensions/tabular/exec/tsconfig.spec.json @@ -0,0 +1,26 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../../dist/out-tsc", + "types": [ + "vitest/globals", + "vitest/importMeta", + "vite/client", + "node", + "vitest" + ] + }, + "include": [ + "vite.config.ts", + "vitest.config.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.test.tsx", + "src/**/*.spec.tsx", + "src/**/*.test.js", + "src/**/*.spec.js", + "src/**/*.test.jsx", + "src/**/*.spec.jsx", + "src/**/*.d.ts" + ] +} diff --git a/libs/extensions/tabular/exec/tsconfig.spec.json.license b/libs/extensions/tabular/exec/tsconfig.spec.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/extensions/tabular/exec/tsconfig.spec.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/tabular/exec/vite.config.ts b/libs/extensions/tabular/exec/vite.config.ts new file mode 100644 index 00000000..3b9aa309 --- /dev/null +++ b/libs/extensions/tabular/exec/vite.config.ts @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// / <reference types='vitest' /> +import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + root: __dirname, + cacheDir: '../../../../node_modules/.vite/libs/extensions/tabular/exec', + + plugins: [nxViteTsPaths()], + test: { + globals: true, + cacheDir: '../../../../node_modules/.vitest', + environment: 'jsdom', + include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], + + reporters: ['default'], + coverage: { + reportsDirectory: '../../../../coverage/libs/extensions/tabular/exec', + provider: 'v8', + }, + }, +}); diff --git a/libs/interpreter-lib/.babelrc b/libs/interpreter-lib/.babelrc new file mode 100644 index 00000000..fd4cbcde --- /dev/null +++ b/libs/interpreter-lib/.babelrc @@ -0,0 +1,10 @@ +{ + "presets": [ + [ + "@nx/js/babel", + { + "useBuiltIns": "usage" + } + ] + ] +} diff --git a/libs/interpreter-lib/.babelrc.license b/libs/interpreter-lib/.babelrc.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/interpreter-lib/.babelrc.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/interpreter-lib/.eslintrc.json b/libs/interpreter-lib/.eslintrc.json new file mode 100644 index 00000000..da5b38f6 --- /dev/null +++ b/libs/interpreter-lib/.eslintrc.json @@ -0,0 +1,21 @@ +{ + "extends": ["../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "parserOptions": { + "project": ["libs/interpreter-lib/tsconfig.lib.json", "libs/interpreter-lib/tsconfig.spec.json"] + }, + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/libs/interpreter-lib/.eslintrc.json.license b/libs/interpreter-lib/.eslintrc.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/interpreter-lib/.eslintrc.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/interpreter-lib/README.md b/libs/interpreter-lib/README.md new file mode 100644 index 00000000..22f5080c --- /dev/null +++ b/libs/interpreter-lib/README.md @@ -0,0 +1,41 @@ +<!-- +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only +--> + +# Jayvee interpreter Library + +This library can be used to interpret a Jayvee model. For example, the [interpreter app](/apps/interpreter/) uses this library after parsing the command line input. + +## Example Usage + +For interpreting a string, you can simply use the method +```javascript +interpretString( + modelString, // string + options, // RunOptions +) +``` + +If you want to interpret anything else, you need to define a function that instantiates the `JayveeModel`, e.g., from a file: +```javascript +const extractAstNodeFn = async ( + services: JayveeServices, + loggerFactory: LoggerFactory, + ) => + await extractAstNodeFromFile<JayveeModel>( + fileName, + services, + loggerFactory.createLogger(), + ); +const exitCode = await interpretModel(extractAstNodeFn, options); +``` + +## Building + +Run `nx build interpreter-lib` to build the library. + +## Running unit tests + +Run `nx test interpreter-lib` to execute the unit tests via [vitest](https://vitest.dev). diff --git a/libs/interpreter-lib/package.json b/libs/interpreter-lib/package.json new file mode 100644 index 00000000..469b2a43 --- /dev/null +++ b/libs/interpreter-lib/package.json @@ -0,0 +1,15 @@ +{ + "name": "@jvalue/jayvee-interpreter-lib", + "bugs": { + "url": "https://github.com/jvalue/jayvee/issues" + }, + "repository": { + "type": "git", + "url": "https://github.com/jvalue/jayvee.git" + }, + "homepage": "https://github.com/jvalue/jayvee", + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org" + } +} diff --git a/libs/interpreter-lib/package.json.license b/libs/interpreter-lib/package.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/interpreter-lib/package.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/interpreter-lib/project.json b/libs/interpreter-lib/project.json new file mode 100644 index 00000000..be170437 --- /dev/null +++ b/libs/interpreter-lib/project.json @@ -0,0 +1,50 @@ +{ + "name": "interpreter-lib", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/interpreter-lib/src", + "projectType": "library", + "targets": { + "build": {}, + "lint": { + "executor": "@nx/eslint:lint", + "outputs": ["{options.outputFile}"] + }, + "test": { + "executor": "@nx/vite:test", + "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], + "options": { + "configFile": "{projectRoot}/vite.config.ts", + "passWithNoTests": false + } + }, + "pre-publish": { + "executor": "nx:run-commands", + "dependsOn": ["build"], + "options": { + "commands": [ + "node tools/scripts/relax-peer-dependency-versions.mjs interpreter-lib", + "node tools/scripts/add-package-json-version.mjs interpreter-lib", + "node tools/scripts/publish.mjs interpreter-lib false" + ], + "parallel": false + } + }, + "publish": { + "executor": "nx:run-commands", + "dependsOn": ["pre-publish"], + "options": { + "commands": ["node tools/scripts/publish.mjs interpreter-lib true"], + "parallel": false + } + }, + "pack": { + "executor": "nx:run-commands", + "dependsOn": ["pre-publish"], + "options": { + "commands": ["node tools/scripts/pack.mjs interpreter-lib"], + "parallel": false + } + } + }, + "tags": [] +} diff --git a/libs/interpreter-lib/project.json.license b/libs/interpreter-lib/project.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/interpreter-lib/project.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/interpreter-lib/src/index.ts b/libs/interpreter-lib/src/index.ts new file mode 100644 index 00000000..1cd7d6ae --- /dev/null +++ b/libs/interpreter-lib/src/index.ts @@ -0,0 +1,8 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +export * from './interpreter'; +export * from './parsing-util'; +export * from './logging'; +export * from './validation-checks'; diff --git a/libs/interpreter-lib/src/interpreter.spec.ts b/libs/interpreter-lib/src/interpreter.spec.ts new file mode 100644 index 00000000..21693721 --- /dev/null +++ b/libs/interpreter-lib/src/interpreter.spec.ts @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { readJvTestAssetHelper } from '@jvalue/jayvee-language-server/test'; + +import { interpretString } from './interpreter'; +import { ExitCode } from './parsing-util'; + +describe('Interpreter', () => { + const readJvTestAsset = readJvTestAssetHelper(__dirname, '../../../'); + + describe('interpretString', () => { + it('should execute a healthy model', async () => { + const exampleFilePath = 'example/cars.jv'; + const model = readJvTestAsset(exampleFilePath); + + const exitCode = await interpretString(model, { + debug: true, + debugGranularity: 'peek', + debugTarget: undefined, + env: new Map(), + }); + expect(exitCode).toEqual(ExitCode.SUCCESS); + }); + }); +}); diff --git a/libs/interpreter-lib/src/interpreter.ts b/libs/interpreter-lib/src/interpreter.ts new file mode 100644 index 00000000..fd475360 --- /dev/null +++ b/libs/interpreter-lib/src/interpreter.ts @@ -0,0 +1,311 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; + +import * as R from '@jvalue/jayvee-execution'; +import { + type DebugGranularity, + DefaultConstraintExtension, + ExecutionContext, + type JayveeConstraintExtension, + type JayveeExecExtension, + type Logger, + executeBlocks, + isDebugGranularity, + logExecutionDuration, + parseValueToInternalRepresentation, +} from '@jvalue/jayvee-execution'; +import { StdExecExtension } from '@jvalue/jayvee-extensions/std/exec'; +import { + type BlockDefinition, + EvaluationContext, + type JayveeModel, + type JayveeServices, + type PipelineDefinition, + type RuntimeParameterProvider, + type WrapperFactoryProvider, + createJayveeServices, + initializeWorkspace, + internalValueToString, +} from '@jvalue/jayvee-language-server'; +import chalk from 'chalk'; +import { NodeFileSystem } from 'langium/node'; + +import { LoggerFactory } from './logging'; +import { ExitCode, extractAstNodeFromString } from './parsing-util'; +import { validateRuntimeParameterLiteral } from './validation-checks'; + +interface InterpreterOptions { + debugGranularity: R.DebugGranularity; + debugTargets: R.DebugTargets; + debug: boolean; + usePolars: boolean; +} + +export interface RunOptions { + env: Map<string, string>; + debug: boolean; + debugGranularity: string; + debugTarget: string | undefined; + usePolars: boolean; + parseOnly?: boolean; +} + +export async function interpretString( + modelString: string, + options: RunOptions, +): Promise<ExitCode> { + const extractAstNodeFn = async ( + services: JayveeServices, + loggerFactory: LoggerFactory, + ) => + await extractAstNodeFromString<JayveeModel>( + modelString, + services, + loggerFactory.createLogger(), + ); + return await interpretModel(extractAstNodeFn, options); +} + +/** + * Parses a model without executing it. + * Also sets up the environment so that the model can be properly executed. + * + * @returns non-null model, services and loggerFactory on success. + */ +export async function parseModel( + extractAstNodeFn: ( + services: JayveeServices, + loggerFactory: LoggerFactory, + ) => Promise<JayveeModel>, + options: RunOptions, +): Promise<{ + model: JayveeModel | null; + loggerFactory: LoggerFactory; + services: JayveeServices | null; +}> { + let services: JayveeServices | null = null; + let model: JayveeModel | null = null; + const loggerFactory = new LoggerFactory(options.debug); + if (!isDebugGranularity(options.debugGranularity)) { + loggerFactory + .createLogger() + .logErr( + `Unknown value "${options.debugGranularity}" for debug granularity option: -dg --debug-granularity.\n` + + `Please use one of the following values: ${R.DebugGranularityValues.join( + ', ', + )}.`, + ); + return { model, services, loggerFactory }; + } + + services = createJayveeServices(NodeFileSystem).Jayvee; + await initializeWorkspace(services); + setupJayveeServices(services, options.env); + + try { + model = await extractAstNodeFn(services, loggerFactory); + return { model, services, loggerFactory }; + } catch (e) { + loggerFactory + .createLogger() + .logErr('Could not extract the AST node of the given model.'); + return { model, services, loggerFactory }; + } +} + +export async function interpretModel( + extractAstNodeFn: ( + services: JayveeServices, + loggerFactory: LoggerFactory, + ) => Promise<JayveeModel>, + options: RunOptions, +): Promise<ExitCode> { + const { model, services, loggerFactory } = await parseModel( + extractAstNodeFn, + options, + ); + + if (model == null || services == null) { + return ExitCode.FAILURE; + } + + const debugTargets = getDebugTargets(options.debugTarget); + + const interpretationExitCode = await interpretJayveeModel( + model, + new StdExecExtension(), + new DefaultConstraintExtension(), + services, + loggerFactory, + { + debug: options.debug, + // type of options.debugGranularity is asserted in parseModel + debugGranularity: options.debugGranularity as DebugGranularity, + debugTargets: debugTargets, + usePolars: options.usePolars, + }, + ); + return interpretationExitCode; +} + +function setupJayveeServices( + services: JayveeServices, + rawRuntimeParameters: ReadonlyMap<string, string>, +) { + setupRuntimeParameterProvider( + services.RuntimeParameterProvider, + rawRuntimeParameters, + ); + + services.validation.ValidationRegistry.registerJayveeValidationChecks({ + RuntimeParameterLiteral: validateRuntimeParameterLiteral, + }); +} + +function setupRuntimeParameterProvider( + runtimeParameterProvider: RuntimeParameterProvider, + rawRuntimeParameters: ReadonlyMap<string, string>, +) { + runtimeParameterProvider.setValueParser(parseValueToInternalRepresentation); + + for (const [key, value] of rawRuntimeParameters.entries()) { + runtimeParameterProvider.setValue(key, value); + } +} + +async function interpretJayveeModel( + model: JayveeModel, + executionExtension: JayveeExecExtension, + constraintExtension: JayveeConstraintExtension, + jayveeServices: JayveeServices, + loggerFactory: LoggerFactory, + runOptions: InterpreterOptions, +): Promise<ExitCode> { + const pipelineRuns: Promise<ExitCode>[] = model.pipelines.map((pipeline) => { + return runPipeline( + pipeline, + executionExtension, + constraintExtension, + jayveeServices, + loggerFactory, + runOptions, + ); + }); + const exitCodes = await Promise.all(pipelineRuns); + + if (exitCodes.includes(ExitCode.FAILURE)) { + return ExitCode.FAILURE; + } + return ExitCode.SUCCESS; +} + +async function runPipeline( + pipeline: PipelineDefinition, + executionExtension: JayveeExecExtension, + constraintExtension: JayveeConstraintExtension, + jayveeServices: JayveeServices, + loggerFactory: LoggerFactory, + runOptions: InterpreterOptions, +): Promise<ExitCode> { + const executionContext = new ExecutionContext( + pipeline, + executionExtension, + constraintExtension, + loggerFactory.createLogger(), + jayveeServices.WrapperFactories, + jayveeServices.ValueTypeProvider, + { + isDebugMode: runOptions.debug, + debugGranularity: runOptions.debugGranularity, + debugTargets: runOptions.debugTargets, + usePolars: runOptions.usePolars, + }, + new EvaluationContext( + jayveeServices.RuntimeParameterProvider, + jayveeServices.operators.EvaluatorRegistry, + jayveeServices.ValueTypeProvider, + ), + ); + + logPipelineOverview( + pipeline, + jayveeServices.RuntimeParameterProvider, + executionContext.logger, + jayveeServices.WrapperFactories, + ); + + const startTime = new Date(); + + const executionResult = await executeBlocks(executionContext, pipeline); + + if (R.isErr(executionResult)) { + const diagnosticError = executionResult.left; + executionContext.logger.logErrDiagnostic( + diagnosticError.message, + diagnosticError.diagnostic, + ); + logExecutionDuration(startTime, executionContext.logger); + return ExitCode.FAILURE; + } + + logExecutionDuration(startTime, executionContext.logger); + return ExitCode.SUCCESS; +} + +export function logPipelineOverview( + pipeline: PipelineDefinition, + runtimeParameterProvider: RuntimeParameterProvider, + logger: Logger, + wrapperFactories: WrapperFactoryProvider, +) { + const pipelineWrapper = wrapperFactories.Pipeline.wrap(pipeline); + + const toString = (block: BlockDefinition, depth = 0): string => { + const blockTypeName = block.type.ref?.name; + assert(blockTypeName !== undefined); + const blockString = `${'\t'.repeat(depth)} -> ${ + block.name + } (${blockTypeName})`; + const childString = pipelineWrapper + .getChildBlocks(block) + .map((child) => toString(child, depth + 1)) + .join('\n'); + return blockString + '\n' + childString; + }; + + const linesBuffer: string[] = []; + + linesBuffer.push(chalk.underline('Overview:')); + + const runtimeParameters = runtimeParameterProvider.getReadonlyMap(); + if (runtimeParameters.size > 0) { + linesBuffer.push(`\tRuntime Parameters (${runtimeParameters.size}):`); + for (const [key, value] of runtimeParameters.entries()) { + linesBuffer.push( + `\t\t${key}: ${internalValueToString(value, wrapperFactories)}`, + ); + } + } + linesBuffer.push( + `\tBlocks (${pipeline.blocks.length} blocks with ${pipeline.pipes.length} pipes):`, + ); + for (const block of pipelineWrapper.getStartingBlocks()) { + linesBuffer.push(toString(block, 1)); + } + logger.logInfo(linesBuffer.join('\n')); +} + +function getDebugTargets( + debugTargetsString: string | undefined, +): R.DebugTargets { + const areAllBlocksTargeted = debugTargetsString === undefined; + if (areAllBlocksTargeted) { + return R.DefaultDebugTargetsValue; + } + + return debugTargetsString.split(',').map((target) => target.trim()); +} diff --git a/libs/interpreter-lib/src/logging/index.ts b/libs/interpreter-lib/src/logging/index.ts new file mode 100644 index 00000000..29070e0f --- /dev/null +++ b/libs/interpreter-lib/src/logging/index.ts @@ -0,0 +1,5 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +export * from './logger-factory'; diff --git a/libs/interpreter-lib/src/logging/logger-factory.ts b/libs/interpreter-lib/src/logging/logger-factory.ts new file mode 100644 index 00000000..9e831cd2 --- /dev/null +++ b/libs/interpreter-lib/src/logging/logger-factory.ts @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { DefaultLogger, type Logger } from '@jvalue/jayvee-execution'; + +export class LoggerFactory { + constructor(private readonly enableDebugLogging: boolean) {} + + createLogger(loggingContext?: string): Logger { + return new DefaultLogger(this.enableDebugLogging, loggingContext); + } +} diff --git a/libs/interpreter-lib/src/parsing-util.spec.ts b/libs/interpreter-lib/src/parsing-util.spec.ts new file mode 100644 index 00000000..a253399e --- /dev/null +++ b/libs/interpreter-lib/src/parsing-util.spec.ts @@ -0,0 +1,179 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import path from 'node:path'; + +import { CachedLogger, DiagnosticSeverity } from '@jvalue/jayvee-execution'; +import { + type JayveeServices, + createJayveeServices, +} from '@jvalue/jayvee-language-server'; +import { + type ParseHelperOptions, + expectNoParserAndLexerErrors, + loadTestExtensions, + parseHelper, + readJvTestAssetHelper, +} from '@jvalue/jayvee-language-server/test'; +import { type AstNode, type LangiumDocument } from 'langium'; +import { NodeFileSystem } from 'langium/node'; + +import { extractDocumentFromFile, validateDocument } from './parsing-util'; + +describe('Validation of parsing-util', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + let services: JayveeServices; + + const logger = new CachedLogger(true, undefined, false); + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../test/assets/parsing-util/', + ); + + beforeAll(async () => { + // Create language services + services = createJayveeServices(NodeFileSystem).Jayvee; + + await loadTestExtensions(services, [ + path.resolve( + __dirname, + '../test/assets/parsing-util/test-extension/TestBlockTypes.jv', + ), + ]); + + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + afterEach(() => { + logger.clearLogs(); + }); + + describe('Function of validateDocument', () => { + async function parseAndValidateDocument(input: string) { + const document = await parse(input); + expectNoParserAndLexerErrors(document); + + return validateDocument(document, services, logger); + } + + it('should diagnose no error on valid document', async () => { + const text = readJvTestAsset('validateDocument/valid-document.jv'); + + await parseAndValidateDocument(text); + + expect(logger.getLogs(DiagnosticSeverity.ERROR)).toHaveLength(0); + expect(logger.getLogs(DiagnosticSeverity.INFO)).toHaveLength(0); + expect(logger.getLogs(DiagnosticSeverity.DEBUG)).toHaveLength(0); + expect(logger.getLogs(DiagnosticSeverity.HINT)).toHaveLength(0); + expect(logger.getLogs(DiagnosticSeverity.WARNING)).toHaveLength(0); + }); + + it('should diagnose error on wrong loader type', async () => { + const text = readJvTestAsset( + 'validateDocument/invalid-wrong-loader-type.jv', + ); + + try { + await parseAndValidateDocument(text); + } catch (e) { + expect(logger.getLogs(DiagnosticSeverity.INFO)).toHaveLength(0); + expect(logger.getLogs(DiagnosticSeverity.ERROR)).toHaveLength(2 * 5); // 2 calls that get formated to 5 lines each + expect(logger.getLogs(DiagnosticSeverity.DEBUG)).toHaveLength(0); + expect(logger.getLogs(DiagnosticSeverity.HINT)).toHaveLength(0); + expect(logger.getLogs(DiagnosticSeverity.WARNING)).toHaveLength(0); + } + }); + + it('should diagnose no error on nonErr diagnostics', async () => { + const text = readJvTestAsset( + 'validateDocument/valid-simplify-warning.jv', + ); + + try { + await parseAndValidateDocument(text); + } catch (e) { + expect(logger.getLogs(DiagnosticSeverity.INFO)).toHaveLength(0); + expect(logger.getLogs(DiagnosticSeverity.ERROR)).toHaveLength(1); + expect(logger.getLogs(DiagnosticSeverity.DEBUG)).toHaveLength(0); + expect(logger.getLogs(DiagnosticSeverity.HINT)).toHaveLength(0); + expect(logger.getLogs(DiagnosticSeverity.WARNING)).toHaveLength(0); + } + }); + }); + + describe('Function of extractDocumentFromFile', () => { + it('should diagnose no error on valid model file', async () => { + await extractDocumentFromFile( + path.resolve( + __dirname, + '../test/assets/parsing-util/', + 'extractDocumentFromFile/valid-model.jv', + ), + services, + logger, + ); + + expect(logger.getLogs(DiagnosticSeverity.INFO)).toHaveLength(0); + expect(logger.getLogs(DiagnosticSeverity.ERROR)).toHaveLength(0); + expect(logger.getLogs(DiagnosticSeverity.DEBUG)).toHaveLength(0); + expect(logger.getLogs(DiagnosticSeverity.HINT)).toHaveLength(0); + expect(logger.getLogs(DiagnosticSeverity.WARNING)).toHaveLength(0); + }); + + it('should diagnose error on invalid extension', async () => { + try { + await extractDocumentFromFile( + path.resolve( + __dirname, + '../test/assets/parsing-util/', + 'extractDocumentFromFile/invalid-extension.lv', + ), + services, + logger, + ); + } catch (e) { + expect(logger.getLogs(DiagnosticSeverity.INFO)).toHaveLength(0); + expect(logger.getLogs(DiagnosticSeverity.ERROR)).toHaveLength(1); + expect(logger.getLogs(DiagnosticSeverity.ERROR)[0]?.message).toEqual( + expect.stringContaining( + 'Please choose a file with this extension: ".jv"', + ), + ); + expect(logger.getLogs(DiagnosticSeverity.DEBUG)).toHaveLength(0); + expect(logger.getLogs(DiagnosticSeverity.HINT)).toHaveLength(0); + expect(logger.getLogs(DiagnosticSeverity.WARNING)).toHaveLength(0); + } + }); + + it('should diagnose error on missing file', async () => { + try { + await extractDocumentFromFile( + path.resolve( + __dirname, + '../test/assets/parsing-util/', + 'extractDocumentFromFile/invalid-missing-file.jv', + ), + services, + logger, + ); + } catch (e) { + expect(logger.getLogs(DiagnosticSeverity.INFO)).toHaveLength(0); + expect(logger.getLogs(DiagnosticSeverity.ERROR)).toHaveLength(1); + expect(logger.getLogs(DiagnosticSeverity.ERROR)[0]?.message).toMatch( + // eslint-disable-next-line no-useless-escape + /File [\w\-\/]*\/libs\/interpreter-lib\/test\/assets\/parsing-util\/extractDocumentFromFile\/invalid-missing-file\.jv does not exist\./, + ); + expect(logger.getLogs(DiagnosticSeverity.DEBUG)).toHaveLength(0); + expect(logger.getLogs(DiagnosticSeverity.HINT)).toHaveLength(0); + expect(logger.getLogs(DiagnosticSeverity.WARNING)).toHaveLength(0); + } + }); + }); +}); diff --git a/libs/interpreter-lib/src/parsing-util.ts b/libs/interpreter-lib/src/parsing-util.ts new file mode 100644 index 00000000..8edccae7 --- /dev/null +++ b/libs/interpreter-lib/src/parsing-util.ts @@ -0,0 +1,112 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import * as fs from 'node:fs'; +import path from 'node:path'; + +import { type Logger } from '@jvalue/jayvee-execution'; +import { initializeWorkspace } from '@jvalue/jayvee-language-server'; +import { type AstNode, type LangiumDocument } from 'langium'; +import { type LangiumServices } from 'langium/lsp'; +import { DiagnosticSeverity } from 'vscode-languageserver-protocol'; +import { URI } from 'vscode-uri'; + +export enum ExitCode { + SUCCESS = 0, + FAILURE = 1, +} + +export async function extractDocumentFromFile( + fileName: string, + services: LangiumServices, + logger: Logger, +): Promise<LangiumDocument> { + const extensions = services.LanguageMetaData.fileExtensions; + if (!extensions.includes(path.extname(fileName))) { + const errorMessage = `Please choose a file with ${ + extensions.length === 1 ? 'this extension' : 'one of these extensions' + }: ${extensions.map((extension) => `"${extension}"`).join(',')}`; + + logger.logErr(errorMessage); + return Promise.reject(ExitCode.FAILURE); + } + + if (!fs.existsSync(fileName)) { + logger.logErr(`File ${fileName} does not exist.`); + return Promise.reject(ExitCode.FAILURE); + } + + const document = + await services.shared.workspace.LangiumDocuments.getOrCreateDocument( + URI.file(path.resolve(fileName)), + ); + + await initializeWorkspace(services); + + return await validateDocument(document, services, logger); +} + +export async function extractDocumentFromString( + modelString: string, + services: LangiumServices, + logger: Logger, +): Promise<LangiumDocument> { + const document = services.shared.workspace.LangiumDocumentFactory.fromString( + modelString, + URI.parse('memory://jayvee.document'), + ); + + await initializeWorkspace(services); + + return await validateDocument(document, services, logger); +} + +export async function validateDocument( + document: LangiumDocument, + services: LangiumServices, + logger: Logger, +): Promise<LangiumDocument> { + await services.shared.workspace.DocumentBuilder.build([document], { + validation: true, + }); + + const diagnostics = document.diagnostics ?? []; + + const errDiagnostics = diagnostics.filter( + (diagnostic) => diagnostic.severity === DiagnosticSeverity.Error, + ); + if (errDiagnostics.length > 0) { + for (const errDiagnostic of errDiagnostics) { + logger.logLanguageServerDiagnostic(errDiagnostic, document); + } + return Promise.reject(ExitCode.FAILURE); + } + + const nonErrDiagnostics = diagnostics.filter( + (diagnostic) => diagnostic.severity !== DiagnosticSeverity.Error, + ); + for (const nonErrDiagnostic of nonErrDiagnostics) { + logger.logLanguageServerDiagnostic(nonErrDiagnostic, document); + } + + return document; +} + +export async function extractAstNodeFromFile<T extends AstNode>( + fileName: string, + services: LangiumServices, + logger: Logger, +): Promise<T> { + return (await extractDocumentFromFile(fileName, services, logger)).parseResult + .value as T; +} + +export async function extractAstNodeFromString<T extends AstNode>( + modelString: string, + services: LangiumServices, + logger: Logger, +): Promise<T> { + return (await extractDocumentFromString(modelString, services, logger)) + .parseResult.value as T; +} diff --git a/libs/interpreter-lib/src/std-extension.spec.ts b/libs/interpreter-lib/src/std-extension.spec.ts new file mode 100644 index 00000000..aa30d965 --- /dev/null +++ b/libs/interpreter-lib/src/std-extension.spec.ts @@ -0,0 +1,45 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; + +import { StdExecExtension } from '@jvalue/jayvee-extensions/std/exec'; +import { + type BlockTypeWrapper, + createJayveeServices, + getAllBuiltinBlockTypes, + initializeWorkspace, +} from '@jvalue/jayvee-language-server'; +import { NodeFileSystem } from 'langium/node'; + +async function loadAllBuiltinBlockTypes(): Promise<BlockTypeWrapper[]> { + const services = createJayveeServices(NodeFileSystem).Jayvee; + await initializeWorkspace(services); + return getAllBuiltinBlockTypes( + services.shared.workspace.LangiumDocuments, + services.WrapperFactories, + ); +} + +describe('std extension', () => { + it('should provide matching block executors for built-in block types', async () => { + (await loadAllBuiltinBlockTypes()).forEach( + (blockType: BlockTypeWrapper) => { + const execExtension = new StdExecExtension(); + console.info(`Looking for executor for block type ${blockType.type}`); + const matchingBlockExecutorClass = + execExtension.getExecutorForBlockType(blockType.type); + + expect(matchingBlockExecutorClass).toBeDefined(); + assert(matchingBlockExecutorClass !== undefined); + + const matchingBlockExecutor = new matchingBlockExecutorClass(); + + expect(matchingBlockExecutor.inputType).toEqual(blockType.inputType); + expect(matchingBlockExecutor.outputType).toEqual(blockType.outputType); + }, + ); + }); +}); diff --git a/libs/interpreter-lib/src/validation-checks/index.ts b/libs/interpreter-lib/src/validation-checks/index.ts new file mode 100644 index 00000000..a2f1d888 --- /dev/null +++ b/libs/interpreter-lib/src/validation-checks/index.ts @@ -0,0 +1,5 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +export * from './runtime-parameter-literal'; diff --git a/libs/interpreter-lib/src/validation-checks/runtime-parameter-literal.spec.ts b/libs/interpreter-lib/src/validation-checks/runtime-parameter-literal.spec.ts new file mode 100644 index 00000000..e9b922b9 --- /dev/null +++ b/libs/interpreter-lib/src/validation-checks/runtime-parameter-literal.spec.ts @@ -0,0 +1,167 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import path from 'node:path'; + +import { parseValueToInternalRepresentation } from '@jvalue/jayvee-execution'; +import { + DefaultOperatorEvaluatorRegistry, + DefaultOperatorTypeComputerRegistry, + EvaluationContext, + type JayveeServices, + type RuntimeParameterLiteral, + RuntimeParameterProvider, + ValidationContext, + ValueTypeProvider, + WrapperFactoryProvider, + createJayveeServices, +} from '@jvalue/jayvee-language-server'; +import { + type ParseHelperOptions, + expectNoParserAndLexerErrors, + loadTestExtensions, + parseHelper, + readJvTestAssetHelper, + validationAcceptorMockImpl, +} from '@jvalue/jayvee-language-server/test'; +import { + type AstNode, + type AstNodeLocator, + type LangiumDocument, +} from 'langium'; +import { NodeFileSystem } from 'langium/node'; +import { vi } from 'vitest'; + +import { validateRuntimeParameterLiteral } from './runtime-parameter-literal'; + +describe('Validation of validateRuntimeParameterLiteral', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + let locator: AstNodeLocator; + let services: JayveeServices; + + const validationAcceptorMock = vi.fn(validationAcceptorMockImpl); + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../test/assets/', + ); + + async function parseAndValidateRuntimeParameterLiteral( + input: string, + runtimeParameters?: Map<string, string>, + ) { + const document = await parse(input); + expectNoParserAndLexerErrors(document); + + const runtimeParameter = locator.getAstNode<RuntimeParameterLiteral>( + document.parseResult.value, + 'pipelines@0/blocks@0/body/properties@0/value', + ) as RuntimeParameterLiteral; + + const runtimeProvider = new RuntimeParameterProvider(); + runtimeProvider.setValueParser(parseValueToInternalRepresentation); + if (runtimeParameters) { + for (const [key, value] of runtimeParameters.entries()) { + runtimeProvider.setValue(key, value); + } + } + + const operatorEvaluatorRegistry = new DefaultOperatorEvaluatorRegistry(); + const valueTypeProvider = new ValueTypeProvider(); + const wrapperFactories = new WrapperFactoryProvider( + operatorEvaluatorRegistry, + valueTypeProvider, + ); + const operatorTypeComputerRegistry = + new DefaultOperatorTypeComputerRegistry( + valueTypeProvider, + wrapperFactories, + ); + + validateRuntimeParameterLiteral(runtimeParameter, { + validationContext: new ValidationContext( + validationAcceptorMock, + operatorTypeComputerRegistry, + ), + evaluationContext: new EvaluationContext( + runtimeProvider, + operatorEvaluatorRegistry, + valueTypeProvider, + ), + valueTypeProvider: valueTypeProvider, + wrapperFactories: wrapperFactories, + }); + } + + beforeAll(async () => { + // Create language services + services = createJayveeServices(NodeFileSystem).Jayvee; + + await loadTestExtensions(services, [ + path.resolve( + __dirname, + '../../test/assets/runtime-parameter-literal/test-extension/TestBlockTypes.jv', + ), + ]); + locator = services.workspace.AstNodeLocator; + + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + afterEach(() => { + // Reset mock + validationAcceptorMock.mockReset(); + }); + + it('should diagnose no error on valid runtime parameter value', async () => { + const text = readJvTestAsset( + 'runtime-parameter-literal/valid-text-runtime-property.jv', + ); + + await parseAndValidateRuntimeParameterLiteral( + text, + new Map([['TEXT_ENV', 'Value 1']]), + ); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(0); + }); + + it('should diagnose error on failed runtime parameter value parsing', async () => { + const text = readJvTestAsset( + 'runtime-parameter-literal/valid-integer-runtime-property.jv', + ); + + await parseAndValidateRuntimeParameterLiteral( + text, + new Map([['INTEGER_ENV', 'Value 1']]), + ); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + `Unable to parse the value "Value 1" as integer.`, + expect.any(Object), + ); + }); + + it('should diagnose error on missing runtime parameter value', async () => { + const text = readJvTestAsset( + 'runtime-parameter-literal/valid-text-runtime-property.jv', + ); + + await parseAndValidateRuntimeParameterLiteral(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + `A value needs to be provided by adding "-e TEXT_ENV=<value>" to the command.`, + expect.any(Object), + ); + }); +}); diff --git a/libs/interpreter-lib/src/validation-checks/runtime-parameter-literal.ts b/libs/interpreter-lib/src/validation-checks/runtime-parameter-literal.ts new file mode 100644 index 00000000..6342ada6 --- /dev/null +++ b/libs/interpreter-lib/src/validation-checks/runtime-parameter-literal.ts @@ -0,0 +1,106 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type BuiltinConstrainttypeDefinition, + type JayveeValidationProps, + type PropertyBody, + type ReferenceableBlockTypeDefinition, + type RuntimeParameterLiteral, +} from '@jvalue/jayvee-language-server'; +import { type Reference } from 'langium'; + +export function validateRuntimeParameterLiteral( + runtimeParameter: RuntimeParameterLiteral, + props: JayveeValidationProps, +) { + checkRuntimeParameterValuePresence(runtimeParameter, props); + if (props.validationContext.hasErrorOccurred()) { + return; + } + + checkRuntimeParameterValueParsing(runtimeParameter, props); +} + +function checkRuntimeParameterValuePresence( + runtimeParameter: RuntimeParameterLiteral, + props: JayveeValidationProps, +) { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + const runtimeParameterName = runtimeParameter?.name; + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (runtimeParameterName === undefined) { + return; + } + + if ( + !props.evaluationContext.hasValueForRuntimeParameter(runtimeParameterName) + ) { + props.validationContext.accept( + 'error', + `A value needs to be provided by adding "-e ${runtimeParameterName}=<value>" to the command.`, + { node: runtimeParameter }, + ); + } +} + +function checkRuntimeParameterValueParsing( + runtimeParameter: RuntimeParameterLiteral, + props: JayveeValidationProps, +) { + const enclosingPropertyBody = getEnclosingPropertyBody(runtimeParameter); + const type: + | Reference<ReferenceableBlockTypeDefinition> + | Reference<BuiltinConstrainttypeDefinition> + | undefined = + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + enclosingPropertyBody.$container?.type; + const wrapper = props.wrapperFactories.TypedObject.wrap(type); + + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + const propertyName = runtimeParameter.$container?.name; + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (propertyName === undefined) { + return; + } + + const propertySpec = wrapper?.getPropertySpecification(propertyName); + if (propertySpec === undefined) { + return; + } + + const valueType = propertySpec.type; + + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + const runtimeParameterName = runtimeParameter?.name; + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (runtimeParameterName === undefined) { + return; + } + + const runtimeParameterValue = + props.evaluationContext.getValueForRuntimeParameter( + runtimeParameterName, + valueType, + ); + if (runtimeParameterValue === undefined) { + const rawValue = + props.evaluationContext.runtimeParameterProvider.getRawValue( + runtimeParameterName, + ); + props.validationContext.accept( + 'error', + `Unable to parse the value "${ + rawValue ?? '' + }" as ${valueType.getName()}.`, + { node: runtimeParameter }, + ); + } +} + +function getEnclosingPropertyBody( + runtimeParameter: RuntimeParameterLiteral, +): PropertyBody { + return runtimeParameter.$container.$container; +} diff --git a/libs/interpreter-lib/test/assets/parsing-util/extractDocumentFromFile/invalid-extension.lv b/libs/interpreter-lib/test/assets/parsing-util/extractDocumentFromFile/invalid-extension.lv new file mode 100644 index 00000000..70f3794d --- /dev/null +++ b/libs/interpreter-lib/test/assets/parsing-util/extractDocumentFromFile/invalid-extension.lv @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestLoader oftype TestFileLoader { + } + + TestExtractor -> TestLoader; +} diff --git a/libs/interpreter-lib/test/assets/parsing-util/extractDocumentFromFile/valid-model.jv b/libs/interpreter-lib/test/assets/parsing-util/extractDocumentFromFile/valid-model.jv new file mode 100644 index 00000000..70f3794d --- /dev/null +++ b/libs/interpreter-lib/test/assets/parsing-util/extractDocumentFromFile/valid-model.jv @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestLoader oftype TestFileLoader { + } + + TestExtractor -> TestLoader; +} diff --git a/libs/interpreter-lib/test/assets/parsing-util/extractDocumentFromString/invalid-model.jv b/libs/interpreter-lib/test/assets/parsing-util/extractDocumentFromString/invalid-model.jv new file mode 100644 index 00000000..7abf8d0f --- /dev/null +++ b/libs/interpreter-lib/test/assets/parsing-util/extractDocumentFromString/invalid-model.jv @@ -0,0 +1,15 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestLoader oftype TestFileLoader { + } + sta + + TestExtractor -> TestLoader; +} diff --git a/libs/interpreter-lib/test/assets/parsing-util/extractDocumentFromString/valid-model.jv b/libs/interpreter-lib/test/assets/parsing-util/extractDocumentFromString/valid-model.jv new file mode 100644 index 00000000..70f3794d --- /dev/null +++ b/libs/interpreter-lib/test/assets/parsing-util/extractDocumentFromString/valid-model.jv @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestLoader oftype TestFileLoader { + } + + TestExtractor -> TestLoader; +} diff --git a/libs/interpreter-lib/test/assets/parsing-util/test-extension/TestBlockTypes.jv b/libs/interpreter-lib/test/assets/parsing-util/test-extension/TestBlockTypes.jv new file mode 100644 index 00000000..bf5bb435 --- /dev/null +++ b/libs/interpreter-lib/test/assets/parsing-util/test-extension/TestBlockTypes.jv @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +builtin blocktype TestFileExtractor { + input inPort oftype None; + output outPort oftype File; +} + +builtin blocktype TestFileLoader { + input inPort oftype File; + output outPort oftype None; +} + +builtin blocktype TestTableLoader { + input inPort oftype Table; + output outPort oftype None; +} diff --git a/libs/interpreter-lib/test/assets/parsing-util/validateDocument/invalid-wrong-loader-type.jv b/libs/interpreter-lib/test/assets/parsing-util/validateDocument/invalid-wrong-loader-type.jv new file mode 100644 index 00000000..037e7501 --- /dev/null +++ b/libs/interpreter-lib/test/assets/parsing-util/validateDocument/invalid-wrong-loader-type.jv @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestLoader oftype TestTableLoader { + } + + TestExtractor -> TestLoader; +} diff --git a/libs/interpreter-lib/test/assets/parsing-util/validateDocument/valid-document.jv b/libs/interpreter-lib/test/assets/parsing-util/validateDocument/valid-document.jv new file mode 100644 index 00000000..70f3794d --- /dev/null +++ b/libs/interpreter-lib/test/assets/parsing-util/validateDocument/valid-document.jv @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestLoader oftype TestFileLoader { + } + + TestExtractor -> TestLoader; +} diff --git a/libs/interpreter-lib/test/assets/parsing-util/validateDocument/valid-simplify-warning.jv b/libs/interpreter-lib/test/assets/parsing-util/validateDocument/valid-simplify-warning.jv new file mode 100644 index 00000000..c2d19130 --- /dev/null +++ b/libs/interpreter-lib/test/assets/parsing-util/validateDocument/valid-simplify-warning.jv @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestPropertyBlock oftype TestProperty { + textProperty: "Test"; + integerProperty: 20 + 20; + } + + block TestLoader oftype TestTableLoader { + } + + TestExtractor -> TestPropertyBlock -> TestLoader; +} + +builtin blocktype TestProperty { + input inPort oftype File; + output outPort oftype Table; + + property integerProperty oftype integer: 0; + property decimalProperty oftype integer: 0.0; + property textProperty oftype text; + property booleanProperty oftype boolean: false; + property regexProperty oftype Regex: /\r?\n/; + property textCollectionProperty oftype Collection<text>: []; + property valueTypeAssignmentProperty oftype ValuetypeAssignment: "test" oftype text; +} diff --git a/libs/interpreter-lib/test/assets/runtime-parameter-literal/test-extension/TestBlockTypes.jv b/libs/interpreter-lib/test/assets/runtime-parameter-literal/test-extension/TestBlockTypes.jv new file mode 100644 index 00000000..75e51a92 --- /dev/null +++ b/libs/interpreter-lib/test/assets/runtime-parameter-literal/test-extension/TestBlockTypes.jv @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +builtin blocktype TestProperty { + input inPort oftype File; + output outPort oftype Table; + + property integerProperty oftype integer: 0; + property decimalProperty oftype integer: 0.0; + property textProperty oftype text; + property booleanProperty oftype boolean: false; + property regexProperty oftype Regex: /\r?\n/; + property textCollectionProperty oftype Collection<text>: []; + property valueTypeAssignmentProperty oftype ValuetypeAssignment: "test" oftype text; +} diff --git a/libs/interpreter-lib/test/assets/runtime-parameter-literal/valid-integer-runtime-property.jv b/libs/interpreter-lib/test/assets/runtime-parameter-literal/valid-integer-runtime-property.jv new file mode 100644 index 00000000..34239be6 --- /dev/null +++ b/libs/interpreter-lib/test/assets/runtime-parameter-literal/valid-integer-runtime-property.jv @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype TestProperty { + integerProperty: requires INTEGER_ENV; + } +} diff --git a/libs/interpreter-lib/test/assets/runtime-parameter-literal/valid-text-runtime-property.jv b/libs/interpreter-lib/test/assets/runtime-parameter-literal/valid-text-runtime-property.jv new file mode 100644 index 00000000..66b1b7e5 --- /dev/null +++ b/libs/interpreter-lib/test/assets/runtime-parameter-literal/valid-text-runtime-property.jv @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype TestProperty { + textProperty: requires TEXT_ENV; + } +} diff --git a/libs/interpreter-lib/tsconfig.json b/libs/interpreter-lib/tsconfig.json new file mode 100644 index 00000000..0e8e67d5 --- /dev/null +++ b/libs/interpreter-lib/tsconfig.json @@ -0,0 +1,21 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/libs/interpreter-lib/tsconfig.json.license b/libs/interpreter-lib/tsconfig.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/interpreter-lib/tsconfig.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/interpreter-lib/tsconfig.lib.json b/libs/interpreter-lib/tsconfig.lib.json new file mode 100644 index 00000000..ce3b531c --- /dev/null +++ b/libs/interpreter-lib/tsconfig.lib.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "declaration": true, + "types": ["node"] + }, + "include": ["src/**/*.ts"], + "exclude": ["vite.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] +} diff --git a/libs/interpreter-lib/tsconfig.lib.json.license b/libs/interpreter-lib/tsconfig.lib.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/interpreter-lib/tsconfig.lib.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/interpreter-lib/tsconfig.spec.json b/libs/interpreter-lib/tsconfig.spec.json new file mode 100644 index 00000000..3c002c21 --- /dev/null +++ b/libs/interpreter-lib/tsconfig.spec.json @@ -0,0 +1,26 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "types": [ + "vitest/globals", + "vitest/importMeta", + "vite/client", + "node", + "vitest" + ] + }, + "include": [ + "vite.config.ts", + "vitest.config.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.test.tsx", + "src/**/*.spec.tsx", + "src/**/*.test.js", + "src/**/*.spec.js", + "src/**/*.test.jsx", + "src/**/*.spec.jsx", + "src/**/*.d.ts" + ] +} diff --git a/libs/interpreter-lib/tsconfig.spec.json.license b/libs/interpreter-lib/tsconfig.spec.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/interpreter-lib/tsconfig.spec.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/interpreter-lib/vite.config.ts b/libs/interpreter-lib/vite.config.ts new file mode 100644 index 00000000..b08e3baf --- /dev/null +++ b/libs/interpreter-lib/vite.config.ts @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// / <reference types='vitest' /> +import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + root: __dirname, + cacheDir: '../../node_modules/.vite/libs/interpreter-lib', + + plugins: [nxViteTsPaths()], + + test: { + globals: true, + cacheDir: '../../node_modules/.vitest', + environment: 'jsdom', + include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], + + reporters: ['default'], + coverage: { + reportsDirectory: '../../coverage/libs/interpreter-lib', + provider: 'v8', + }, + }, +}); diff --git a/libs/language-server/.babelrc b/libs/language-server/.babelrc new file mode 100644 index 00000000..fd4cbcde --- /dev/null +++ b/libs/language-server/.babelrc @@ -0,0 +1,10 @@ +{ + "presets": [ + [ + "@nx/js/babel", + { + "useBuiltIns": "usage" + } + ] + ] +} diff --git a/libs/language-server/.babelrc.license b/libs/language-server/.babelrc.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/language-server/.babelrc.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/language-server/.eslintrc.json b/libs/language-server/.eslintrc.json new file mode 100644 index 00000000..8c95ea13 --- /dev/null +++ b/libs/language-server/.eslintrc.json @@ -0,0 +1,21 @@ +{ + "extends": ["../../.eslintrc.json"], + "ignorePatterns": ["!**/*", "src/lib/**/generated"], + "parserOptions": { + "project": ["libs/language-server/tsconfig.lib.json", "libs/language-server/tsconfig.spec.json"] + }, + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/libs/language-server/.eslintrc.json.license b/libs/language-server/.eslintrc.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/language-server/.eslintrc.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/language-server/.gitignore b/libs/language-server/.gitignore new file mode 100644 index 00000000..1f17e0dd --- /dev/null +++ b/libs/language-server/.gitignore @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +# +# SPDX-License-Identifier: AGPL-3.0-only + +# files generated by Langium +src/lib/ast/generated +src/lib/builtin-library/generated diff --git a/libs/language-server/README.md b/libs/language-server/README.md new file mode 100644 index 00000000..b3e975f6 --- /dev/null +++ b/libs/language-server/README.md @@ -0,0 +1,27 @@ +<!-- +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only +--> + +# Language Server + +## Important project files + +- `language-configuration.json` - the language configuration used in monaco editors, defining the tokens that are used for comments and brackets. +- `src/grammar/` - a folder containing the grammar definition of the language. +- `langium-config.json` - the configuration for the Langium generator. + +## FAQ + +### Why is the linting rule `@typescript-eslint/no-unnecessary-condition` sometimes disabled? + +The rule is disabled in places, where we work with potentially incomplete AST nodes. + +Langium generates separate interfaces for each kind of AST node. +According to the generated interfaces, their properties are never `undefined` but in reality they might be. + +So, in order to write safe code, we need to handle those cases where properties may be `undefined` and thus use +conditions for checking the actual value against `undefined`. +ESLint considers such conditions unnecessary (due to the interfaces), which is the reason for disabling that particular +rule. diff --git a/libs/language-server/langium-config.json b/libs/language-server/langium-config.json new file mode 100644 index 00000000..d411e964 --- /dev/null +++ b/libs/language-server/langium-config.json @@ -0,0 +1,20 @@ +{ + "projectName": "Jayvee", + "languages": [ + { + "id": "jayvee", + "grammar": "src/grammar/main.langium", + "fileExtensions": [".jv"], + "textMate": { + "out": "../../apps/vs-code-extension/assets/jayvee.tmLanguage.json" + }, + "monarch": { + "out": "../../libs/monaco-editor/src/lib/jayvee.monarch.ts" + }, + "prism": { + "out": "../../apps/docs/src/theme/prism-jayvee.js" + } + } + ], + "out": "src/lib/ast/generated" +} diff --git a/libs/language-server/langium-config.json.license b/libs/language-server/langium-config.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/language-server/langium-config.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/language-server/package.json b/libs/language-server/package.json new file mode 100644 index 00000000..9c864b5a --- /dev/null +++ b/libs/language-server/package.json @@ -0,0 +1,16 @@ +{ + "name": "@jvalue/jayvee-language-server", + "main": "./src/index.js", + "bugs": { + "url": "https://github.com/jvalue/jayvee/issues" + }, + "repository": { + "type": "git", + "url": "https://github.com/jvalue/jayvee.git" + }, + "homepage": "https://github.com/jvalue/jayvee", + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org" + } +} diff --git a/libs/language-server/package.json.license b/libs/language-server/package.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/language-server/package.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/language-server/project.json b/libs/language-server/project.json new file mode 100644 index 00000000..4005729a --- /dev/null +++ b/libs/language-server/project.json @@ -0,0 +1,62 @@ +{ + "name": "language-server", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/language-server/src", + "projectType": "library", + "targets": { + "generate": { + "executor": "nx:run-commands", + "options": { + "commands": [ + "langium generate -f libs/language-server/langium-config.json", + // Workaround until https://github.com/langium/langium/issues/740 is resolved: + "node tools/scripts/fix-monarch-grammar-escape.mjs", + "node tools/scripts/language-server/generate-stdlib.mjs" + ], + "parallel": false + } + }, + "build": {}, + "lint": { + "executor": "@nx/eslint:lint", + "outputs": ["{options.outputFile}"] + }, + "test": { + "executor": "@nx/vite:test", + "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], + "options": { + "configFile": "{projectRoot}/vite.config.ts", + "passWithNoTests": false + } + }, + "pre-publish": { + "executor": "nx:run-commands", + "dependsOn": ["build"], + "options": { + "commands": [ + "node tools/scripts/relax-peer-dependency-versions.mjs language-server", + "node tools/scripts/add-package-json-version.mjs language-server", + "node tools/scripts/publish.mjs language-server false" + ], + "parallel": false + } + }, + "publish": { + "executor": "nx:run-commands", + "dependsOn": ["pre-publish"], + "options": { + "commands": ["node tools/scripts/publish.mjs language-server true"], + "parallel": false + } + }, + "pack": { + "executor": "nx:run-commands", + "dependsOn": ["pre-publish"], + "options": { + "commands": ["node tools/scripts/pack.mjs language-server"], + "parallel": false + } + } + }, + "tags": [] +} diff --git a/libs/language-server/project.json.license b/libs/language-server/project.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/language-server/project.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/language-server/src/grammar/block-type.langium b/libs/language-server/src/grammar/block-type.langium new file mode 100644 index 00000000..11281327 --- /dev/null +++ b/libs/language-server/src/grammar/block-type.langium @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import './expression' +import './io-type' +import './terminal' +import './transform' +import './value-type' +import './block' +import './main' + +ReferenceableBlockTypeDefinition: + BuiltinBlockTypeDefinition | CompositeBlockTypeDefinition; + +BuiltinBlockTypeDefinition: + 'builtin' 'blocktype' name=ID '{' + (inputs+=BlockTypeInput | outputs+=BlockTypeOutput | properties+=BlockTypeProperty)* +'}'; + +CompositeBlockTypeDefinition: + 'composite' 'blocktype' name=ID '{' + ( + inputs+=BlockTypeInput + | outputs+=BlockTypeOutput + | properties+=BlockTypeProperty + | blocks+=BlockDefinition + | pipes+=BlockTypePipeline + | transforms+=TransformDefinition + )* +'}'; + +BlockTypeInput: + 'input' name=ID 'oftype' iotype=[IotypeDefinition] ';'; + +BlockTypeOutput: + 'output' name=ID 'oftype' iotype=[IotypeDefinition] ';'; + +BlockTypeProperty: + 'property' name=ID 'oftype' valueType=ValueTypeReference (':' defaultValue=Expression)? ';'; + +BlockTypePipeline: + input=[BlockTypeInput] '->' (blocks+=[BlockDefinition] '->')+ output=[BlockTypeOutput] ';'; \ No newline at end of file diff --git a/libs/language-server/src/grammar/block.langium b/libs/language-server/src/grammar/block.langium new file mode 100644 index 00000000..30163527 --- /dev/null +++ b/libs/language-server/src/grammar/block.langium @@ -0,0 +1,10 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import './property' +import './block-type' +import './terminal' + +BlockDefinition: + 'block' name=ID 'oftype' type=[ReferenceableBlockTypeDefinition] body=PropertyBody; \ No newline at end of file diff --git a/libs/language-server/src/grammar/cell-range.langium b/libs/language-server/src/grammar/cell-range.langium new file mode 100644 index 00000000..df8ce7cb --- /dev/null +++ b/libs/language-server/src/grammar/cell-range.langium @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import './terminal' + +CellRangeLiteral: + RangeLiteral + | ColumnLiteral + | RowLiteral + | CellLiteral; + +RangeLiteral: + 'range' cellFrom=(CELL_REFERENCE | CellReference) ':' cellTo=(CELL_REFERENCE | CellReference); + +ColumnLiteral: + 'column' columnId=ColumnId; + +RowLiteral: + 'row' rowId=RowId; + +CellLiteral: + 'cell' cellId=(CELL_REFERENCE | CellReference); + +CellReference: + columnId=ColumnId rowId=RowId; + +ColumnId: + value=(ID | '*'); + +RowId: + value=(INTEGER | '*'); \ No newline at end of file diff --git a/libs/language-server/src/grammar/constraint.langium b/libs/language-server/src/grammar/constraint.langium new file mode 100644 index 00000000..375c2f56 --- /dev/null +++ b/libs/language-server/src/grammar/constraint.langium @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import './property' +import './terminal' +import './value-type' +import './expression' + +ConstraintDefinition: + TypedConstraintDefinition | ExpressionConstraintDefinition; + +TypedConstraintDefinition: + 'constraint' name=ID 'oftype' type=[BuiltinConstrainttypeDefinition] body=PropertyBody; + +ExpressionConstraintDefinition: + 'constraint' name=ID 'on' valueType=ValueTypeReference ':' expression=Expression ';'; + +BuiltinConstrainttypeDefinition: + 'builtin' 'constrainttype' name=ID 'on' valueType=ValueTypeReference '{' + (properties+=ConstrainttypeProperty)* +'}'; + +ConstrainttypeProperty: + 'property' name=ID 'oftype' valueType=ValueTypeReference (':' defaultValue=Expression)? ';'; \ No newline at end of file diff --git a/libs/language-server/src/grammar/expression.langium b/libs/language-server/src/grammar/expression.langium new file mode 100644 index 00000000..d5873068 --- /dev/null +++ b/libs/language-server/src/grammar/expression.langium @@ -0,0 +1,98 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import './block-type' +import './cell-range' +import './constraint' +import './value-type' +import './terminal' +import './transform' + +Expression: + ReplaceExpression; + +// The nesting of the following rules implies the precedence of the operators: +ReplaceExpression infers Expression: + OrExpression ({infer TernaryExpression.first=current} operator='replace' second=OrExpression 'with' third=OrExpression)*; + +OrExpression infers Expression: + AndExpression ({infer BinaryExpression.left=current} operator='or' right=AndExpression)*; + +AndExpression infers Expression: + XorExpression ({infer BinaryExpression.left=current} operator='and' right=XorExpression)*; + +XorExpression infers Expression: + EqualityExpression ({infer BinaryExpression.left=current} operator='xor' right=EqualityExpression)*; + +EqualityExpression infers Expression: + MatchesExpression ({infer BinaryExpression.left=current} operator=('==' | '!=') right=MatchesExpression)*; + +MatchesExpression infers Expression: + InExpression ({infer BinaryExpression.left=current} operator='matches' right=InExpression)*; + +InExpression infers Expression: + RelationalExpression ({infer BinaryExpression.left=current} operator='in' right=RelationalExpression)*; + +RelationalExpression infers Expression: + AdditiveExpression ({infer BinaryExpression.left=current} operator=('<' | '<=' | '>' | '>=') right=AdditiveExpression)*; + +AdditiveExpression infers Expression: + MultiplicativeExpression ({infer BinaryExpression.left=current} operator=('+' | '-') right=MultiplicativeExpression)*; + +MultiplicativeExpression infers Expression: + ExponentialExpression ({infer BinaryExpression.left=current} operator=('*' | '/' | '%') right=ExponentialExpression)*; + +ExponentialExpression infers Expression: + PrimaryExpression ({infer BinaryExpression.left=current} operator=('pow' | 'root') right=PrimaryExpression)*; + +PrimaryExpression infers Expression: + '(' Expression ')' + | UnaryExpression + | ExpressionLiteral; + +UnaryExpression: + operator=('not' | '+' | '-' | 'sqrt' | 'floor' | 'ceil' | 'round' | 'lowercase' | 'uppercase' | 'asDecimal' | 'asInteger' | 'asBoolean' | 'asText') expression=PrimaryExpression; + +ExpressionLiteral: + ValueLiteral | FreeVariableLiteral; + +ValueLiteral: + TextLiteral + | NumericLiteral + | BooleanLiteral + | RegexLiteral + | CellRangeLiteral + | ValuetypeAssignmentLiteral + | CollectionLiteral; + +TextLiteral: + value=STRING; + +NumericLiteral: + value=(INTEGER) + | value=(DECIMAL); + +BooleanLiteral: + value?='true' | 'false'; + +RegexLiteral: + value=REGEX; + +CollectionLiteral: + '[' (values+=(Expression) (',' values+=(Expression))*)? ','? ']'; + +FreeVariableLiteral: + ValueKeywordLiteral | ReferenceLiteral; + +ValueKeywordLiteral: + 'value' ('.' lengthAccess?='length')?; + +ReferenceLiteral: + value=[Referencable]; + +Referencable: + ConstraintDefinition + | BlockTypeProperty + | TransformDefinition + | TransformPortDefinition; diff --git a/libs/language-server/src/grammar/io-type.langium b/libs/language-server/src/grammar/io-type.langium new file mode 100644 index 00000000..5b962709 --- /dev/null +++ b/libs/language-server/src/grammar/io-type.langium @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import './expression' +import './terminal' + +IotypeDefinition: + 'builtin' 'iotype' name=ID ';'; diff --git a/libs/language-server/src/grammar/main.langium b/libs/language-server/src/grammar/main.langium new file mode 100644 index 00000000..6e05e129 --- /dev/null +++ b/libs/language-server/src/grammar/main.langium @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +grammar Jayvee + +import './block-type' +import './pipeline' +import './terminal' +import './property' +import './value-type' +import './constraint' +import './transform' +import './io-type' + +entry JayveeModel: + ( + pipelines+=PipelineDefinition + | valueTypes+=(CustomValuetypeDefinition | BuiltinValuetypeDefinition) + | constraints+=ConstraintDefinition + | transforms+=TransformDefinition + | blockTypes+=ReferenceableBlockTypeDefinition + | constrainttypes+=BuiltinConstrainttypeDefinition + | iotypes+=IotypeDefinition + )*; + + diff --git a/libs/language-server/src/grammar/pipeline.langium b/libs/language-server/src/grammar/pipeline.langium new file mode 100644 index 00000000..c31949d8 --- /dev/null +++ b/libs/language-server/src/grammar/pipeline.langium @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import './block' +import './transform' +import './value-type' +import './constraint' +import './terminal' + +PipelineDefinition: + 'pipeline' name=ID '{' + ( + blocks+=BlockDefinition + | pipes+=PipeDefinition + | valueTypes+=CustomValuetypeDefinition + | constraints+=ConstraintDefinition + | transforms+=TransformDefinition + )* + '}'; + +PipeDefinition: + blocks+=[BlockDefinition] ('->' blocks+=[BlockDefinition])+ ';'; \ No newline at end of file diff --git a/libs/language-server/src/grammar/property.langium b/libs/language-server/src/grammar/property.langium new file mode 100644 index 00000000..85e3231f --- /dev/null +++ b/libs/language-server/src/grammar/property.langium @@ -0,0 +1,15 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import './terminal'; +import './expression'; + +PropertyBody: + '{' (properties+=PropertyAssignment)* '}'; + +PropertyAssignment: + name=ID ':' value=(Expression | RuntimeParameterLiteral) ';'; + +RuntimeParameterLiteral: + 'requires' name=ID; diff --git a/libs/language-server/src/grammar/terminal.langium b/libs/language-server/src/grammar/terminal.langium new file mode 100644 index 00000000..6112374b --- /dev/null +++ b/libs/language-server/src/grammar/terminal.langium @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// The order of the terminals matters here, it implicitly defines the prioritization for the lexer. +// For more details, see https://langium.org/docs/grammar-language/#terminal-rules + +terminal CELL_REFERENCE: /[A-Z]+/ INTEGER; +terminal ID: /[_a-zA-Z][\w_]*/; + +terminal DECIMAL returns number: INTEGER '.' INTEGER; +terminal INTEGER returns number: /[0-9]+/; + +terminal STRING: /"[^"]*"|'[^']*'/; + +hidden terminal SL_COMMENT: /\/\/[^\n\r]*/; +hidden terminal ML_COMMENT: /\/\*[\s\S]*?\*\//; + +terminal REGEX: /\/.+\//; + +hidden terminal WHITESPACE: /\s+/; diff --git a/libs/language-server/src/grammar/transform.langium b/libs/language-server/src/grammar/transform.langium new file mode 100644 index 00000000..d58f5a32 --- /dev/null +++ b/libs/language-server/src/grammar/transform.langium @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import 'terminal' +import 'value-type' +import 'expression' + +TransformDefinition: + 'transform' name=ID body=TransformBody; + +TransformBody: + '{' (ports+=TransformPortDefinition)* (outputAssignments+=TransformOutputAssignment)* '}'; + +TransformPortDefinition: + kind=('from' | 'to') name=ID 'oftype' valueType=ValueTypeReference ';'; + +TransformOutputAssignment: + outPortName=[TransformPortDefinition] ':' expression=Expression ';'; diff --git a/libs/language-server/src/grammar/value-type.langium b/libs/language-server/src/grammar/value-type.langium new file mode 100644 index 00000000..f861b1c7 --- /dev/null +++ b/libs/language-server/src/grammar/value-type.langium @@ -0,0 +1,39 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import './expression' +import './terminal' + +BuiltinValuetypeDefinition infers ValuetypeDefinition: + isBuiltin?='builtin' 'valuetype' name=ID + (genericDefinition=ValuetypeGenericsDefinition)? + ';'; + +CustomValuetypeDefinition infers ValuetypeDefinition: + 'valuetype' name=ID + (genericDefinition=ValuetypeGenericsDefinition)? + 'oftype' type=ValueTypeReference '{' + 'constraints' ':' constraints=CollectionLiteral ';' + '}'; + +ValuetypeAssignmentLiteral: + value=ValuetypeAssignment; + +ValuetypeAssignment: + name=STRING 'oftype' type=ValueTypeReference; + +ValueTypeReference: + reference=[ValuetypeDefinition] + ('<' + genericRefs+=[ValuetypeDefinition] (',' genericRefs+=[ValuetypeDefinition])* + '>')?; + +ValuetypeGenericsDefinition: + '<' + generics+=ValuetypeGenericDefinition + (',' generics+=ValuetypeGenericDefinition)* + '>'; + +ValuetypeGenericDefinition: + name=ID; \ No newline at end of file diff --git a/libs/language-server/src/index.ts b/libs/language-server/src/index.ts new file mode 100644 index 00000000..ec01a8e2 --- /dev/null +++ b/libs/language-server/src/index.ts @@ -0,0 +1,5 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +export * from './lib'; diff --git a/libs/language-server/src/lib/ast/expressions/evaluate-expression.ts b/libs/language-server/src/lib/ast/expressions/evaluate-expression.ts new file mode 100644 index 00000000..0810b8c5 --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/evaluate-expression.ts @@ -0,0 +1,262 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; + +import { assertUnreachable } from 'langium'; +import { pl } from 'nodejs-polars'; + +import { type ValidationContext } from '../../validation'; +import { + type BinaryExpression, + type Expression, + type PropertyAssignment, + type TernaryExpression, + type UnaryExpression, + type ValueLiteral, + isBinaryExpression, + isBlockTypeProperty, + isCellRangeLiteral, + isCollectionLiteral, + isExpression, + isExpressionLiteral, + isFreeVariableLiteral, + isRegexLiteral, + isRuntimeParameterLiteral, + isTernaryExpression, + isUnaryExpression, + isValueLiteral, +} from '../generated/ast'; +import { type ValueType, type WrapperFactoryProvider } from '../wrappers'; + +import { type EvaluationContext } from './evaluation-context'; +import { EvaluationStrategy } from './evaluation-strategy'; +import { + type InternalValueRepresentation, + type PolarsInternal, +} from './internal-value-representation'; +import { + type OperatorEvaluator, + type PolarsOperatorEvaluator, +} from './operator-evaluator'; +import { + INTERNAL_VALUE_REPRESENTATION_TYPEGUARD, + isEveryValueDefined, +} from './typeguards'; + +export function evaluatePropertyValue<T extends InternalValueRepresentation>( + property: PropertyAssignment, + evaluationContext: EvaluationContext, + wrapperFactories: WrapperFactoryProvider, + valueType: ValueType<T>, +): T | undefined { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + const propertyValue = property?.value; + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + assert(propertyValue !== undefined); + + if (isBlockTypeProperty(propertyValue)) { + // Properties of block types are always undefined + // because they are set in the block that instantiates the block type + return undefined; + } + + let result: InternalValueRepresentation | undefined; + if (isRuntimeParameterLiteral(propertyValue)) { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + const runtimeParameterName = propertyValue?.name; + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (runtimeParameterName === undefined) { + result = undefined; + } else { + result = evaluationContext.getValueForRuntimeParameter( + runtimeParameterName, + valueType, + ); + } + } else if (isExpression(propertyValue)) { + result = evaluateExpression( + propertyValue, + evaluationContext, + wrapperFactories, + ); + } else { + assertUnreachable(propertyValue); + } + + assert( + result === undefined || valueType.isInternalValueRepresentation(result), + `Evaluation result ${ + result?.toString() ?? 'undefined' + } is not valid: Neither undefined, nor of type ${valueType.getName()}`, + ); + return result; +} + +function getEvaluator( + expression: UnaryExpression | BinaryExpression | TernaryExpression, + evaluationContext: EvaluationContext, +): PolarsOperatorEvaluator< + UnaryExpression | BinaryExpression | TernaryExpression +> { + if (isUnaryExpression(expression)) { + const operator = expression.operator; + return evaluationContext.operatorRegistry.unary[operator]; + } + if (isBinaryExpression(expression)) { + const operator = expression.operator; + return evaluationContext.operatorRegistry.binary[operator]; + } + if (isTernaryExpression(expression)) { + const operator = expression.operator; + throw new Error('Ternary operations are not supported yet'); + // return evaluationContext.operatorRegistry.ternary[operator]; + } + assertUnreachable(expression); +} + +export function polarsEvaluateExpression( + expression: Expression | undefined, + evaluationContext: EvaluationContext, + wrapperFactories: WrapperFactoryProvider, + context: ValidationContext | undefined = undefined, + strategy: EvaluationStrategy = EvaluationStrategy.LAZY, +): PolarsInternal | undefined { + if (expression === undefined) { + return undefined; + } + if (isExpressionLiteral(expression)) { + if (isFreeVariableLiteral(expression)) { + const fv = evaluationContext.getValueFor(expression); + if (INTERNAL_VALUE_REPRESENTATION_TYPEGUARD(fv)) { + return pl.lit(fv); + } + return fv; + } else if (isValueLiteral(expression)) { + const lit = evaluateValueLiteral( + expression, + evaluationContext, + wrapperFactories, + context, + strategy, + ); + if (lit === undefined) { + return undefined; + } + return pl.lit(lit); + } + assertUnreachable(expression); + } + const evaluator = getEvaluator(expression, evaluationContext); + + return evaluator.polarsEvaluate( + expression, + evaluationContext, + wrapperFactories, + strategy, + context, + ); +} + +export function evaluateExpression( + expression: Expression | undefined, + evaluationContext: EvaluationContext, + wrapperFactories: WrapperFactoryProvider, + context: ValidationContext | undefined = undefined, + strategy: EvaluationStrategy = EvaluationStrategy.LAZY, +): InternalValueRepresentation | undefined { + if (expression === undefined) { + return undefined; + } + if (isExpressionLiteral(expression)) { + if (isFreeVariableLiteral(expression)) { + const fv = evaluationContext.getValueFor(expression); + assert(fv === undefined || INTERNAL_VALUE_REPRESENTATION_TYPEGUARD(fv)); + return fv; + } else if (isValueLiteral(expression)) { + return evaluateValueLiteral( + expression, + evaluationContext, + wrapperFactories, + context, + strategy, + ); + } + assertUnreachable(expression); + } + if (isUnaryExpression(expression)) { + const operator = expression.operator; + const evaluator = evaluationContext.operatorRegistry.unary[operator]; + return evaluator.evaluate( + expression, + evaluationContext, + wrapperFactories, + strategy, + context, + ); + } + if (isBinaryExpression(expression)) { + const operator = expression.operator; + const evaluator = evaluationContext.operatorRegistry.binary[operator]; + return evaluator.evaluate( + expression, + evaluationContext, + wrapperFactories, + strategy, + context, + ); + } + if (isTernaryExpression(expression)) { + const operator = expression.operator; + const evaluator = evaluationContext.operatorRegistry.ternary[operator]; + return evaluator.evaluate( + expression, + evaluationContext, + wrapperFactories, + strategy, + context, + ); + } + assertUnreachable(expression); +} + +function evaluateValueLiteral( + expression: ValueLiteral, + evaluationContext: EvaluationContext, + wrapperFactories: WrapperFactoryProvider, + validationContext: ValidationContext | undefined = undefined, + strategy: EvaluationStrategy = EvaluationStrategy.LAZY, +): InternalValueRepresentation | undefined { + if (isCollectionLiteral(expression)) { + const evaluatedCollection = expression.values.map((v) => + evaluateExpression( + v, + evaluationContext, + wrapperFactories, + validationContext, + strategy, + ), + ); + if (!isEveryValueDefined(evaluatedCollection)) { + return undefined; + } + return evaluatedCollection; + } + if (isCellRangeLiteral(expression)) { + if (!wrapperFactories.CellRange.canWrap(expression)) { + return undefined; + } + return expression; + } + if (isRegexLiteral(expression)) { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (expression?.value === undefined) { + return undefined; + } + return new RegExp(expression.value); + } + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + return expression?.value; +} diff --git a/libs/language-server/src/lib/ast/expressions/evaluation-context.ts b/libs/language-server/src/lib/ast/expressions/evaluation-context.ts new file mode 100644 index 00000000..9f018a8d --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/evaluation-context.ts @@ -0,0 +1,128 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; + +import { assertUnreachable } from 'langium'; +import { type pl } from 'nodejs-polars'; + +import { type RuntimeParameterProvider } from '../../services'; +import {} from '../../validation/validation-context'; +import { + type FreeVariableLiteral, + type ReferenceLiteral, + type ValueKeywordLiteral, + isBlockTypeProperty, + isConstraintDefinition, + isReferenceLiteral, + isTransformDefinition, + isTransformPortDefinition, + isValueKeywordLiteral, +} from '../generated/ast'; +import { type ValueTypeProvider } from '../wrappers'; +import { type ValueType } from '../wrappers/value-type/value-type'; + +import { type InternalValueRepresentation } from './internal-value-representation'; +import { type OperatorEvaluatorRegistry } from './operator-registry'; + +export class EvaluationContext { + private readonly variableValues = new Map< + string, + InternalValueRepresentation | pl.Expr + >(); + private valueKeywordValue: InternalValueRepresentation | undefined = + undefined; + + constructor( + public readonly runtimeParameterProvider: RuntimeParameterProvider, + public readonly operatorRegistry: OperatorEvaluatorRegistry, + public readonly valueTypeProvider: ValueTypeProvider, + ) {} + + getValueFor( + literal: FreeVariableLiteral, + ): InternalValueRepresentation | pl.Expr | undefined { + if (isReferenceLiteral(literal)) { + return this.getValueForReference(literal); + } else if (isValueKeywordLiteral(literal)) { + return this.getValueForValueKeyword(literal, this.valueTypeProvider); + } + assertUnreachable(literal); + } + + setValueForReference( + refText: string, + value: InternalValueRepresentation | pl.Expr, + ): void { + this.variableValues.set(refText, value); + } + + deleteValueForReference(refText: string): void { + this.variableValues.delete(refText); + } + + getValueForReference( + referenceLiteral: ReferenceLiteral, + ): InternalValueRepresentation | pl.Expr | undefined { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + const dereferenced = referenceLiteral?.value?.ref; + if (dereferenced === undefined) { + return undefined; + } + + if (isConstraintDefinition(dereferenced)) { + return dereferenced; + } + if (isTransformDefinition(dereferenced)) { + return dereferenced; + } + if (isTransformPortDefinition(dereferenced)) { + return this.variableValues.get(dereferenced.name); + } + if (isBlockTypeProperty(dereferenced)) { + return this.variableValues.get(dereferenced.name); + } + assertUnreachable(dereferenced); + } + + hasValueForRuntimeParameter(key: string): boolean { + return this.runtimeParameterProvider.hasValue(key); + } + + getValueForRuntimeParameter<I extends InternalValueRepresentation>( + key: string, + valueType: ValueType<I>, + ): I | undefined { + return this.runtimeParameterProvider.getParsedValue(key, valueType); + } + + setValueForValueKeyword(value: InternalValueRepresentation) { + this.valueKeywordValue = value; + } + + deleteValueForValueKeyword() { + this.valueKeywordValue = undefined; + } + + getValueForValueKeyword( + literal: ValueKeywordLiteral, + valueTypeProvider: ValueTypeProvider, + ): InternalValueRepresentation | undefined { + if (this.valueKeywordValue === undefined) { + return undefined; + } + + if (literal.lengthAccess) { + assert( + valueTypeProvider.Primitives.Text.isInternalValueRepresentation( + this.valueKeywordValue, + ), + ); + return this.valueKeywordValue.length; + } + + return this.valueKeywordValue; + } +} diff --git a/libs/language-server/src/lib/ast/expressions/evaluation-strategy.ts b/libs/language-server/src/lib/ast/expressions/evaluation-strategy.ts new file mode 100644 index 00000000..44086c7a --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/evaluation-strategy.ts @@ -0,0 +1,8 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +export enum EvaluationStrategy { + EXHAUSTIVE, + LAZY, +} diff --git a/libs/language-server/src/lib/ast/expressions/evaluators/addition-operator-evaluator.ts b/libs/language-server/src/lib/ast/expressions/evaluators/addition-operator-evaluator.ts new file mode 100644 index 00000000..d1d451b6 --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/evaluators/addition-operator-evaluator.ts @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type PolarsInternal } from '../internal-value-representation'; +import { DefaultBinaryOperatorEvaluator } from '../operator-evaluator'; +import { NUMBER_TYPEGUARD } from '../typeguards'; + +export class AdditionOperatorEvaluator extends DefaultBinaryOperatorEvaluator< + number, + number, + number +> { + constructor() { + super('+', NUMBER_TYPEGUARD, NUMBER_TYPEGUARD); + } + override doEvaluate(leftValue: number, rightValue: number): number { + return leftValue + rightValue; + } + override polarsDoEvaluate( + left: PolarsInternal, + right: PolarsInternal, + ): PolarsInternal { + return left.plus(right); + } +} diff --git a/libs/language-server/src/lib/ast/expressions/evaluators/and-operator-evaluator.ts b/libs/language-server/src/lib/ast/expressions/evaluators/and-operator-evaluator.ts new file mode 100644 index 00000000..7453871c --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/evaluators/and-operator-evaluator.ts @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type PolarsInternal } from '../internal-value-representation'; +import { BooleanShortCircuitOperatorEvaluator } from '../operator-evaluator'; + +export class AndOperatorEvaluator extends BooleanShortCircuitOperatorEvaluator { + constructor() { + super('and'); + } + override canSkipRightOperandEvaluation(leftValue: boolean): boolean { + return leftValue === false; + } + + override getShortCircuitValue(): boolean { + return false; + } + + override doEvaluate(leftValue: boolean, rightValue: boolean): boolean { + return leftValue && rightValue; + } + override polarsDoEvaluate( + leftValue: PolarsInternal, + rightValue: PolarsInternal, + ): PolarsInternal { + return leftValue.and(rightValue); + } +} diff --git a/libs/language-server/src/lib/ast/expressions/evaluators/ceil-operator-evaluator.ts b/libs/language-server/src/lib/ast/expressions/evaluators/ceil-operator-evaluator.ts new file mode 100644 index 00000000..3a94e733 --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/evaluators/ceil-operator-evaluator.ts @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type PolarsInternal } from '../internal-value-representation'; +import { DefaultUnaryOperatorEvaluator } from '../operator-evaluator'; +import { NUMBER_TYPEGUARD } from '../typeguards'; + +export class CeilOperatorEvaluator extends DefaultUnaryOperatorEvaluator< + number, + number +> { + constructor() { + super('ceil', NUMBER_TYPEGUARD); + } + override doEvaluate(operandValue: number): number { + return Math.ceil(operandValue); + } + + override polarsDoEvaluate(col: PolarsInternal): PolarsInternal { + return col.ceil(); + } +} diff --git a/libs/language-server/src/lib/ast/expressions/evaluators/division-operator-evaluator.ts b/libs/language-server/src/lib/ast/expressions/evaluators/division-operator-evaluator.ts new file mode 100644 index 00000000..258cd355 --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/evaluators/division-operator-evaluator.ts @@ -0,0 +1,48 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; + +import { pl } from 'nodejs-polars'; +import { type ValidationContext } from '../../../validation/validation-context'; +import { type BinaryExpression } from '../../generated/ast'; +import { type PolarsInternal } from '../internal-value-representation'; +import { DefaultBinaryOperatorEvaluator } from '../operator-evaluator'; +import { NUMBER_TYPEGUARD } from '../typeguards'; + +export class DivisionOperatorEvaluator extends DefaultBinaryOperatorEvaluator< + number, + number, + number +> { + constructor() { + super('/', NUMBER_TYPEGUARD, NUMBER_TYPEGUARD); + } + + override doEvaluate( + leftValue: number, + rightValue: number, + expression: BinaryExpression, + context: ValidationContext | undefined, + ): number | undefined { + const resultingValue = leftValue / rightValue; + + if (!isFinite(resultingValue)) { + assert(rightValue === 0); + context?.accept('error', 'Arithmetic error: division by zero', { + node: expression, + }); + return undefined; + } + return resultingValue; + } + + override polarsDoEvaluate( + left: PolarsInternal, + right: PolarsInternal, + ): PolarsInternal { + return left.div(right); + } +} diff --git a/libs/language-server/src/lib/ast/expressions/evaluators/equality-operator-evaluator.ts b/libs/language-server/src/lib/ast/expressions/evaluators/equality-operator-evaluator.ts new file mode 100644 index 00000000..bd194369 --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/evaluators/equality-operator-evaluator.ts @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type InternalValueRepresentation, + type PolarsInternal, +} from '../internal-value-representation'; +import { DefaultBinaryOperatorEvaluator } from '../operator-evaluator'; +import { INTERNAL_VALUE_REPRESENTATION_TYPEGUARD } from '../typeguards'; + +export class EqualityOperatorEvaluator extends DefaultBinaryOperatorEvaluator< + InternalValueRepresentation, + InternalValueRepresentation, + boolean +> { + constructor() { + super( + '==', + INTERNAL_VALUE_REPRESENTATION_TYPEGUARD, + INTERNAL_VALUE_REPRESENTATION_TYPEGUARD, + ); + } + override doEvaluate( + left: InternalValueRepresentation, + right: InternalValueRepresentation, + ): boolean { + return left === right; + } + override polarsDoEvaluate( + left: PolarsInternal, + right: PolarsInternal, + ): PolarsInternal { + return left.eq(right); + } +} diff --git a/libs/language-server/src/lib/ast/expressions/evaluators/floor-operator-evaluator.ts b/libs/language-server/src/lib/ast/expressions/evaluators/floor-operator-evaluator.ts new file mode 100644 index 00000000..3a9a5a2e --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/evaluators/floor-operator-evaluator.ts @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type PolarsInternal } from '../internal-value-representation'; +import { DefaultUnaryOperatorEvaluator } from '../operator-evaluator'; +import { NUMBER_TYPEGUARD } from '../typeguards'; + +export class FloorOperatorEvaluator extends DefaultUnaryOperatorEvaluator< + number, + number +> { + constructor() { + super('floor', NUMBER_TYPEGUARD); + } + override doEvaluate(operandValue: number): number { + return Math.floor(operandValue); + } + + override polarsDoEvaluate(col: PolarsInternal): PolarsInternal { + return col.floor(); + } +} diff --git a/libs/language-server/src/lib/ast/expressions/evaluators/greater-equal-operator-evaluator.ts b/libs/language-server/src/lib/ast/expressions/evaluators/greater-equal-operator-evaluator.ts new file mode 100644 index 00000000..2625eeba --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/evaluators/greater-equal-operator-evaluator.ts @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type PolarsInternal } from '../internal-value-representation'; +import { DefaultBinaryOperatorEvaluator } from '../operator-evaluator'; +import { NUMBER_TYPEGUARD } from '../typeguards'; + +export class GreaterEqualOperatorEvaluator extends DefaultBinaryOperatorEvaluator< + number, + number, + boolean +> { + constructor() { + super('>=', NUMBER_TYPEGUARD, NUMBER_TYPEGUARD); + } + override doEvaluate(leftValue: number, rightValue: number): boolean { + return leftValue >= rightValue; + } + override polarsDoEvaluate( + left: PolarsInternal, + right: PolarsInternal, + ): PolarsInternal { + return left.gtEq(right); + } +} diff --git a/libs/language-server/src/lib/ast/expressions/evaluators/greater-than-operator-evaluator.ts b/libs/language-server/src/lib/ast/expressions/evaluators/greater-than-operator-evaluator.ts new file mode 100644 index 00000000..2139950b --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/evaluators/greater-than-operator-evaluator.ts @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type PolarsInternal } from '../internal-value-representation'; +import { DefaultBinaryOperatorEvaluator } from '../operator-evaluator'; +import { NUMBER_TYPEGUARD } from '../typeguards'; + +export class GreaterThanOperatorEvaluator extends DefaultBinaryOperatorEvaluator< + number, + number, + boolean +> { + constructor() { + super('>', NUMBER_TYPEGUARD, NUMBER_TYPEGUARD); + } + override doEvaluate(leftValue: number, rightValue: number): boolean { + return leftValue > rightValue; + } + override polarsDoEvaluate( + left: PolarsInternal, + right: PolarsInternal, + ): PolarsInternal { + return left.gt(right); + } +} diff --git a/libs/language-server/src/lib/ast/expressions/evaluators/in-operator-evaluator.ts b/libs/language-server/src/lib/ast/expressions/evaluators/in-operator-evaluator.ts new file mode 100644 index 00000000..9f031f2b --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/evaluators/in-operator-evaluator.ts @@ -0,0 +1,45 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type InternalValueRepresentation, + type InternalValueRepresentationTypeguard, +} from '../internal-value-representation'; +import { DefaultBinaryOperatorEvaluator } from '../operator-evaluator'; +import { NUMBER_TYPEGUARD, STRING_TYPEGUARD } from '../typeguards'; + +export class InOperatorEvaluator extends DefaultBinaryOperatorEvaluator< + InternalValueRepresentation, + InternalValueRepresentation[], + boolean +> { + constructor() { + super( + 'in', + isLeftOperandMatchingValueRepresentationTypeguard, + isRightOperandMatchingValueRepresentationTypeguard, + ); + } + override doEvaluate( + left: string | number, + right: (string | number)[], + ): boolean { + return right.includes(left); + } +} + +const isLeftOperandMatchingValueRepresentationTypeguard: InternalValueRepresentationTypeguard< + string | number +> = (value: unknown): value is string | number => { + return STRING_TYPEGUARD(value) || NUMBER_TYPEGUARD(value); +}; + +const isRightOperandMatchingValueRepresentationTypeguard: InternalValueRepresentationTypeguard< + (string | number)[] +> = (value: unknown): value is (string | number)[] => { + return ( + Array.isArray(value) && + value.every(isLeftOperandMatchingValueRepresentationTypeguard) + ); +}; diff --git a/libs/language-server/src/lib/ast/expressions/evaluators/inequality-operator-evaluator.ts b/libs/language-server/src/lib/ast/expressions/evaluators/inequality-operator-evaluator.ts new file mode 100644 index 00000000..50d976ab --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/evaluators/inequality-operator-evaluator.ts @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type InternalValueRepresentation, + type PolarsInternal, +} from '../internal-value-representation'; +import { DefaultBinaryOperatorEvaluator } from '../operator-evaluator'; +import { INTERNAL_VALUE_REPRESENTATION_TYPEGUARD } from '../typeguards'; + +export class InequalityOperatorEvaluator extends DefaultBinaryOperatorEvaluator< + InternalValueRepresentation, + InternalValueRepresentation, + boolean +> { + constructor() { + super( + '!=', + INTERNAL_VALUE_REPRESENTATION_TYPEGUARD, + INTERNAL_VALUE_REPRESENTATION_TYPEGUARD, + ); + } + override doEvaluate( + left: InternalValueRepresentation, + right: InternalValueRepresentation, + ): boolean { + return left !== right; + } + override polarsDoEvaluate( + left: PolarsInternal, + right: PolarsInternal, + ): PolarsInternal { + return left.neq(right); + } +} diff --git a/libs/language-server/src/lib/ast/expressions/evaluators/less-equal-operator-evaluator.ts b/libs/language-server/src/lib/ast/expressions/evaluators/less-equal-operator-evaluator.ts new file mode 100644 index 00000000..ba0d5883 --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/evaluators/less-equal-operator-evaluator.ts @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type PolarsInternal } from '../internal-value-representation'; +import { DefaultBinaryOperatorEvaluator } from '../operator-evaluator'; +import { NUMBER_TYPEGUARD } from '../typeguards'; + +export class LessEqualOperatorEvaluator extends DefaultBinaryOperatorEvaluator< + number, + number, + boolean +> { + constructor() { + super('<=', NUMBER_TYPEGUARD, NUMBER_TYPEGUARD); + } + override doEvaluate(leftValue: number, rightValue: number): boolean { + return leftValue <= rightValue; + } + override polarsDoEvaluate( + left: PolarsInternal, + right: PolarsInternal, + ): PolarsInternal { + return left.ltEq(right); + } +} diff --git a/libs/language-server/src/lib/ast/expressions/evaluators/less-than-operator-evaluator.ts b/libs/language-server/src/lib/ast/expressions/evaluators/less-than-operator-evaluator.ts new file mode 100644 index 00000000..ca1ce6f4 --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/evaluators/less-than-operator-evaluator.ts @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type PolarsInternal } from '../internal-value-representation'; +import { DefaultBinaryOperatorEvaluator } from '../operator-evaluator'; +import { NUMBER_TYPEGUARD } from '../typeguards'; + +export class LessThanOperatorEvaluator extends DefaultBinaryOperatorEvaluator< + number, + number, + boolean +> { + constructor() { + super('<', NUMBER_TYPEGUARD, NUMBER_TYPEGUARD); + } + override doEvaluate(leftValue: number, rightValue: number): boolean { + return leftValue < rightValue; + } + override polarsDoEvaluate( + left: PolarsInternal, + right: PolarsInternal, + ): PolarsInternal { + return left.lt(right); + } +} diff --git a/libs/language-server/src/lib/ast/expressions/evaluators/lowercase-operator-evaluator.spec.ts b/libs/language-server/src/lib/ast/expressions/evaluators/lowercase-operator-evaluator.spec.ts new file mode 100644 index 00000000..f0a083fc --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/evaluators/lowercase-operator-evaluator.spec.ts @@ -0,0 +1,15 @@ +// SPDX-FileCopyrightText: 2024 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only +import { executeDefaultTextToTextExpression } from '../test-utils'; + +describe('The lowercase operator', () => { + it('should lowercase alphabetic characters successfully', async () => { + const result = await executeDefaultTextToTextExpression( + 'lowercase inputValue', + 'Hello, World!', + ); + + expect(result).toEqual('hello, world!'); + }); +}); diff --git a/libs/language-server/src/lib/ast/expressions/evaluators/lowercase-operator-evaluator.ts b/libs/language-server/src/lib/ast/expressions/evaluators/lowercase-operator-evaluator.ts new file mode 100644 index 00000000..6c7ac4b5 --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/evaluators/lowercase-operator-evaluator.ts @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2024 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { DefaultUnaryOperatorEvaluator } from '../operator-evaluator'; +import { STRING_TYPEGUARD } from '../typeguards'; + +export class LowercaseOperatorEvaluator extends DefaultUnaryOperatorEvaluator< + string, + string +> { + constructor() { + super('lowercase', STRING_TYPEGUARD); + } + override doEvaluate(operandValue: string): string { + return operandValue.toLowerCase(); + } +} diff --git a/libs/language-server/src/lib/ast/expressions/evaluators/matches-operator-evaluator.ts b/libs/language-server/src/lib/ast/expressions/evaluators/matches-operator-evaluator.ts new file mode 100644 index 00000000..f89b5dea --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/evaluators/matches-operator-evaluator.ts @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { DefaultBinaryOperatorEvaluator } from '../operator-evaluator'; +import { REGEXP_TYPEGUARD, STRING_TYPEGUARD } from '../typeguards'; + +export class MatchesOperatorEvaluator extends DefaultBinaryOperatorEvaluator< + string, + RegExp, + boolean +> { + constructor() { + super('matches', STRING_TYPEGUARD, REGEXP_TYPEGUARD); + } + override doEvaluate(leftValue: string, rightValue: RegExp): boolean { + return rightValue.test(leftValue); + } +} diff --git a/libs/language-server/src/lib/ast/expressions/evaluators/minus-operator-evaluator.ts b/libs/language-server/src/lib/ast/expressions/evaluators/minus-operator-evaluator.ts new file mode 100644 index 00000000..aafff203 --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/evaluators/minus-operator-evaluator.ts @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type PolarsInternal } from '../internal-value-representation'; +import { DefaultUnaryOperatorEvaluator } from '../operator-evaluator'; +import { NUMBER_TYPEGUARD } from '../typeguards'; + +export class MinusOperatorEvaluator extends DefaultUnaryOperatorEvaluator< + number, + number +> { + constructor() { + super('-', NUMBER_TYPEGUARD); + } + override doEvaluate(operandValue: number): number { + return -operandValue; + } + protected override polarsDoEvaluate(operand: PolarsInternal): PolarsInternal { + // HACK: Polars does not support neg + return operand.mul(-1); + } +} diff --git a/libs/language-server/src/lib/ast/expressions/evaluators/modulo-operator-evaluator.ts b/libs/language-server/src/lib/ast/expressions/evaluators/modulo-operator-evaluator.ts new file mode 100644 index 00000000..ba211cb5 --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/evaluators/modulo-operator-evaluator.ts @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { ValidationContext } from '../../../validation/validation-context'; +import { type BinaryExpression } from '../../generated/ast'; +import { type PolarsInternal } from '../internal-value-representation'; +import { DefaultBinaryOperatorEvaluator } from '../operator-evaluator'; +import { NUMBER_TYPEGUARD } from '../typeguards'; + +export class ModuloOperatorEvaluator extends DefaultBinaryOperatorEvaluator< + number, + number, + number +> { + constructor() { + super('%', NUMBER_TYPEGUARD, NUMBER_TYPEGUARD); + } + + override doEvaluate( + leftValue: number, + rightValue: number, + expression: BinaryExpression, + context: ValidationContext | undefined, + ): number | undefined { + const resultingValue = leftValue % rightValue; + + if (!isFinite(resultingValue)) { + if (rightValue === 0) { + context?.accept('error', 'Arithmetic error: modulo by zero', { + node: expression, + }); + } else { + context?.accept('error', 'Unknown arithmetic error', { + node: expression, + }); + } + return undefined; + } + return resultingValue; + } + + override polarsDoEvaluate( + left: PolarsInternal, + right: PolarsInternal, + ): PolarsInternal | undefined { + return left.modulo(right); + } +} diff --git a/libs/language-server/src/lib/ast/expressions/evaluators/multiplication-operator-evaluator.ts b/libs/language-server/src/lib/ast/expressions/evaluators/multiplication-operator-evaluator.ts new file mode 100644 index 00000000..18290be5 --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/evaluators/multiplication-operator-evaluator.ts @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type PolarsInternal } from '../internal-value-representation'; +import { DefaultBinaryOperatorEvaluator } from '../operator-evaluator'; +import { NUMBER_TYPEGUARD } from '../typeguards'; + +export class MultiplicationOperatorEvaluator extends DefaultBinaryOperatorEvaluator< + number, + number, + number +> { + constructor() { + super('*', NUMBER_TYPEGUARD, NUMBER_TYPEGUARD); + } + override doEvaluate(leftValue: number, rightValue: number): number { + return leftValue * rightValue; + } + override polarsDoEvaluate( + left: PolarsInternal, + right: PolarsInternal, + ): PolarsInternal { + return left.mul(right); + } +} diff --git a/libs/language-server/src/lib/ast/expressions/evaluators/not-operator-evaluator.ts b/libs/language-server/src/lib/ast/expressions/evaluators/not-operator-evaluator.ts new file mode 100644 index 00000000..d9b9c5d0 --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/evaluators/not-operator-evaluator.ts @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type PolarsInternal } from '../internal-value-representation'; +import { DefaultUnaryOperatorEvaluator } from '../operator-evaluator'; +import { BOOLEAN_TYPEGUARD } from '../typeguards'; + +export class NotOperatorEvaluator extends DefaultUnaryOperatorEvaluator< + boolean, + boolean +> { + constructor() { + super('not', BOOLEAN_TYPEGUARD); + } + override doEvaluate(operandValue: boolean): boolean { + return !operandValue; + } + + override polarsDoEvaluate(operand: PolarsInternal): PolarsInternal { + return operand.not(); + } +} diff --git a/libs/language-server/src/lib/ast/expressions/evaluators/or-operator-evaluator.ts b/libs/language-server/src/lib/ast/expressions/evaluators/or-operator-evaluator.ts new file mode 100644 index 00000000..befb7022 --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/evaluators/or-operator-evaluator.ts @@ -0,0 +1,30 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type PolarsInternal } from '../internal-value-representation'; +import { BooleanShortCircuitOperatorEvaluator } from '../operator-evaluator'; + +export class OrOperatorEvaluator extends BooleanShortCircuitOperatorEvaluator { + constructor() { + super('or'); + } + override canSkipRightOperandEvaluation(leftValue: boolean): boolean { + return leftValue === true; + } + + override getShortCircuitValue(): boolean { + return true; + } + + override doEvaluate(leftValue: boolean, rightValue: boolean): boolean { + return leftValue || rightValue; + } + + override polarsDoEvaluate( + leftValue: PolarsInternal, + rightValue: PolarsInternal, + ): PolarsInternal { + return leftValue.or(rightValue); + } +} diff --git a/libs/language-server/src/lib/ast/expressions/evaluators/parse-operators-evaluators.spec.ts b/libs/language-server/src/lib/ast/expressions/evaluators/parse-operators-evaluators.spec.ts new file mode 100644 index 00000000..5f6e56de --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/evaluators/parse-operators-evaluators.spec.ts @@ -0,0 +1,90 @@ +// SPDX-FileCopyrightText: 2024 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only +import { type InternalValueRepresentation } from '..'; +import { executeDefaultTextToTextExpression } from '../test-utils'; + +async function expectSuccess<I extends InternalValueRepresentation>( + op: string, + input: string, + expected: I, +) { + let result: InternalValueRepresentation | undefined = undefined; + try { + result = await executeDefaultTextToTextExpression( + `${op} inputValue`, + input, + ); + } finally { + expect(result).toEqual(expected); + } +} + +async function expectError(op: string, input: string) { + let result: InternalValueRepresentation | undefined = undefined; + try { + result = await executeDefaultTextToTextExpression( + `${op} inputValue`, + input, + ); + } catch { + result = undefined; + } finally { + expect(result).toBeUndefined(); + } +} + +describe('The asText operator', () => { + it('should parse text successfully', async () => { + await expectSuccess('asText', 'someText', 'someText'); + }); +}); + +describe('The asDecimal operator', () => { + it('should parse positive decimals successfully', async () => { + await expectSuccess('asDecimal', '1.6', 1.6); + }); + + it('should parse decimals with commas successfully', async () => { + await expectSuccess('asDecimal', '1,6', 1.6); + }); + + it('should parse negative decimals with commas successfully', async () => { + await expectSuccess('asDecimal', '-1,6', -1.6); + }); +}); + +describe('The asInteger operator', () => { + it('should parse positive integers successfully', async () => { + await expectSuccess('asInteger', '32', 32); + }); + + it('should parse negative integers successfully', async () => { + await expectSuccess('asInteger', '-1', -1); + }); + + it('should fail with decimal values', async () => { + await expectError('asInteger', '32.5'); + }); +}); + +describe('The asBoolean operator', () => { + it('should parse true and True successfully', async () => { + await expectSuccess('asBoolean', 'true', true); + await expectSuccess('asBoolean', 'True', true); + }); + + it('should parse false and False successfully', async () => { + await expectSuccess('asBoolean', 'false', false); + await expectSuccess('asBoolean', 'False', false); + }); + + it('should fail with 0 and 1', async () => { + await expectError('asBoolean', '0'); + await expectError('asBoolean', '1'); + }); + + it('should fail on a arbitrary string', async () => { + await expectError('asBoolean', 'notABoolean'); + }); +}); diff --git a/libs/language-server/src/lib/ast/expressions/evaluators/parse-operators-evaluators.ts b/libs/language-server/src/lib/ast/expressions/evaluators/parse-operators-evaluators.ts new file mode 100644 index 00000000..920b243e --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/evaluators/parse-operators-evaluators.ts @@ -0,0 +1,70 @@ +// SPDX-FileCopyrightText: 2024 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type ValueTypeProvider } from '../../wrappers'; +import { DefaultUnaryOperatorEvaluator } from '../operator-evaluator'; +import { STRING_TYPEGUARD } from '../typeguards'; + +export class AsTextOperatorEvaluator extends DefaultUnaryOperatorEvaluator< + string, + string +> { + constructor(private readonly valueTypeProvider: ValueTypeProvider) { + super('asText', STRING_TYPEGUARD); + } + override doEvaluate(operandValue: string): string { + return this.valueTypeProvider.Primitives.Text.fromString(operandValue); + } +} + +export class AsDecimalOperatorEvaluator extends DefaultUnaryOperatorEvaluator< + string, + number +> { + constructor(private readonly valueTypeProvider: ValueTypeProvider) { + super('asDecimal', STRING_TYPEGUARD); + } + override doEvaluate(operandValue: string): number { + const dec = + this.valueTypeProvider.Primitives.Decimal.fromString(operandValue); + if (dec === undefined) { + throw new Error(`Could not parse "${operandValue}" into a Decimal`); + } + return dec; + } +} + +export class AsIntegerOperatorEvaluator extends DefaultUnaryOperatorEvaluator< + string, + number +> { + constructor(private readonly valueTypeProvider: ValueTypeProvider) { + super('asInteger', STRING_TYPEGUARD); + } + override doEvaluate(operandValue: string): number { + const int = + this.valueTypeProvider.Primitives.Integer.fromString(operandValue); + if (int === undefined) { + throw new Error(`Could not parse "${operandValue}" into an Integer`); + } + return int; + } +} + +export class AsBooleanOperatorEvaluator extends DefaultUnaryOperatorEvaluator< + string, + boolean +> { + constructor(private readonly valueTypeProvider: ValueTypeProvider) { + super('asBoolean', STRING_TYPEGUARD); + } + override doEvaluate(operandValue: string): boolean { + const bool = + this.valueTypeProvider.Primitives.Boolean.fromString(operandValue); + if (bool === undefined) { + throw new Error(`Could not parse "${operandValue}" into a Boolean`); + } + return bool; + } +} diff --git a/libs/language-server/src/lib/ast/expressions/evaluators/plus-operator-evaluator.ts b/libs/language-server/src/lib/ast/expressions/evaluators/plus-operator-evaluator.ts new file mode 100644 index 00000000..875a50dc --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/evaluators/plus-operator-evaluator.ts @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type PolarsInternal } from '../internal-value-representation'; +import { DefaultUnaryOperatorEvaluator } from '../operator-evaluator'; +import { NUMBER_TYPEGUARD } from '../typeguards'; + +export class PlusOperatorEvaluator extends DefaultUnaryOperatorEvaluator< + number, + number +> { + constructor() { + super('+', NUMBER_TYPEGUARD); + } + override doEvaluate(operandValue: number): number { + return operandValue; + } + + protected override polarsDoEvaluate(operand: PolarsInternal): PolarsInternal { + return operand; + } +} diff --git a/libs/language-server/src/lib/ast/expressions/evaluators/pow-operator-evaluator.ts b/libs/language-server/src/lib/ast/expressions/evaluators/pow-operator-evaluator.ts new file mode 100644 index 00000000..dffaad5f --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/evaluators/pow-operator-evaluator.ts @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type ValidationContext } from '../../../validation/validation-context'; +import { type BinaryExpression } from '../../generated/ast'; +import { type PolarsInternal } from '../internal-value-representation'; +import { DefaultBinaryOperatorEvaluator } from '../operator-evaluator'; +import { NUMBER_TYPEGUARD } from '../typeguards'; + +export class PowOperatorEvaluator extends DefaultBinaryOperatorEvaluator< + number, + number, + number +> { + constructor() { + super('pow', NUMBER_TYPEGUARD, NUMBER_TYPEGUARD); + } + + override doEvaluate( + leftValue: number, + rightValue: number, + expression: BinaryExpression, + context: ValidationContext | undefined, + ): number | undefined { + const resultingValue = leftValue ** rightValue; + + if (!isFinite(resultingValue)) { + if (leftValue === 0 && rightValue < 0) { + context?.accept( + 'error', + 'Arithmetic error: zero raised to a negative number', + { node: expression }, + ); + } else { + context?.accept('error', 'Unknown arithmetic error', { + node: expression, + }); + } + return undefined; + } + return resultingValue; + } +} diff --git a/libs/language-server/src/lib/ast/expressions/evaluators/replace-operator-evaluator.spec.ts b/libs/language-server/src/lib/ast/expressions/evaluators/replace-operator-evaluator.spec.ts new file mode 100644 index 00000000..e27482d7 --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/evaluators/replace-operator-evaluator.spec.ts @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only +import { executeDefaultTextToTextExpression } from '../test-utils'; + +describe('The replace operator', () => { + it('should replace text successfully', async () => { + const result = await executeDefaultTextToTextExpression( + "inputValue replace /Test/ with 'World'", + 'Hello Test', + ); + + expect(result).toEqual('Hello World'); + }); + + it('should be able to replace text with nothing', async () => { + const result = await executeDefaultTextToTextExpression( + "inputValue replace / Test/ with ''", + 'Hello Test', + ); + + expect(result).toEqual('Hello'); + }); +}); diff --git a/libs/language-server/src/lib/ast/expressions/evaluators/replace-operator-evaluator.ts b/libs/language-server/src/lib/ast/expressions/evaluators/replace-operator-evaluator.ts new file mode 100644 index 00000000..675f7346 --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/evaluators/replace-operator-evaluator.ts @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { DefaultTernaryOperatorEvaluator } from '../operator-evaluator'; +import { REGEXP_TYPEGUARD, STRING_TYPEGUARD } from '../typeguards'; + +export class ReplaceOperatorEvaluator extends DefaultTernaryOperatorEvaluator< + string, + RegExp, + string, + string +> { + constructor() { + super('replace', STRING_TYPEGUARD, REGEXP_TYPEGUARD, STRING_TYPEGUARD); + } + override doEvaluate( + firstValue: string, + secondValue: RegExp, + thirdValue: string, + ): string { + return firstValue.replace(secondValue, thirdValue); + } +} diff --git a/libs/language-server/src/lib/ast/expressions/evaluators/root-operator-evaluator.ts b/libs/language-server/src/lib/ast/expressions/evaluators/root-operator-evaluator.ts new file mode 100644 index 00000000..e628e30c --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/evaluators/root-operator-evaluator.ts @@ -0,0 +1,47 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type ValidationContext } from '../../../validation/validation-context'; +import { type BinaryExpression } from '../../generated/ast'; +import { DefaultBinaryOperatorEvaluator } from '../operator-evaluator'; +import { NUMBER_TYPEGUARD } from '../typeguards'; + +export class RootOperatorEvaluator extends DefaultBinaryOperatorEvaluator< + number, + number, + number +> { + constructor() { + super('root', NUMBER_TYPEGUARD, NUMBER_TYPEGUARD); + } + + override doEvaluate( + leftValue: number, + rightValue: number, + expression: BinaryExpression, + context: ValidationContext | undefined, + ): number | undefined { + const resultingValue = leftValue ** (1 / rightValue); + + if (!isFinite(resultingValue)) { + if (leftValue === 0 && rightValue < 0) { + context?.accept( + 'error', + 'Arithmetic error: root of zero with negative degree', + { node: expression }, + ); + } else if (rightValue === 0) { + context?.accept('error', 'Arithmetic error: root of degree zero', { + node: expression, + }); + } else { + context?.accept('error', 'Unknown arithmetic error', { + node: expression, + }); + } + return undefined; + } + return resultingValue; + } +} diff --git a/libs/language-server/src/lib/ast/expressions/evaluators/round-operator-evaluator.ts b/libs/language-server/src/lib/ast/expressions/evaluators/round-operator-evaluator.ts new file mode 100644 index 00000000..1dd73006 --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/evaluators/round-operator-evaluator.ts @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type PolarsInternal } from '../internal-value-representation'; +import { DefaultUnaryOperatorEvaluator } from '../operator-evaluator'; +import { NUMBER_TYPEGUARD } from '../typeguards'; + +export class RoundOperatorEvaluator extends DefaultUnaryOperatorEvaluator< + number, + number +> { + constructor() { + super('round', NUMBER_TYPEGUARD); + } + override doEvaluate(operandValue: number): number { + return Math.round(operandValue); + } + + override polarsDoEvaluate(col: PolarsInternal): PolarsInternal { + return col.round(0); + } +} diff --git a/libs/language-server/src/lib/ast/expressions/evaluators/sqrt-operator-evaluator.ts b/libs/language-server/src/lib/ast/expressions/evaluators/sqrt-operator-evaluator.ts new file mode 100644 index 00000000..467b7aeb --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/evaluators/sqrt-operator-evaluator.ts @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; + +import { type ValidationContext } from '../../../validation/validation-context'; +import { type UnaryExpression } from '../../generated/ast'; +import { type PolarsInternal } from '../internal-value-representation'; +import { DefaultUnaryOperatorEvaluator } from '../operator-evaluator'; +import { NUMBER_TYPEGUARD } from '../typeguards'; + +export class SqrtOperatorEvaluator extends DefaultUnaryOperatorEvaluator< + number, + number +> { + constructor() { + super('sqrt', NUMBER_TYPEGUARD); + } + override doEvaluate( + operandValue: number, + expression: UnaryExpression, + context: ValidationContext | undefined, + ): number | undefined { + const resultingValue = Math.sqrt(operandValue); + + if (!isFinite(resultingValue)) { + assert(operandValue < 0); + context?.accept( + 'error', + 'Arithmetic error: square root of negative number', + { node: expression }, + ); + return undefined; + } + return resultingValue; + } + + protected override polarsDoEvaluate(operand: PolarsInternal): PolarsInternal { + // HACK: Typst does not have a root expression + return operand.pow(1 / 2); + } +} diff --git a/libs/language-server/src/lib/ast/expressions/evaluators/subtraction-operator-evaluator.ts b/libs/language-server/src/lib/ast/expressions/evaluators/subtraction-operator-evaluator.ts new file mode 100644 index 00000000..e35a4dd6 --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/evaluators/subtraction-operator-evaluator.ts @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only +import { type PolarsInternal } from '../internal-value-representation'; +import { DefaultBinaryOperatorEvaluator } from '../operator-evaluator'; +import { NUMBER_TYPEGUARD } from '../typeguards'; + +export class SubtractionOperatorEvaluator extends DefaultBinaryOperatorEvaluator< + number, + number, + number +> { + constructor() { + super('-', NUMBER_TYPEGUARD, NUMBER_TYPEGUARD); + } + override doEvaluate(leftValue: number, rightValue: number): number { + return leftValue - rightValue; + } + override polarsDoEvaluate( + left: PolarsInternal, + right: PolarsInternal, + ): PolarsInternal { + return left.minus(right); + } +} diff --git a/libs/language-server/src/lib/ast/expressions/evaluators/uppercase-operator-evaluator.spec.ts b/libs/language-server/src/lib/ast/expressions/evaluators/uppercase-operator-evaluator.spec.ts new file mode 100644 index 00000000..0d7d68b0 --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/evaluators/uppercase-operator-evaluator.spec.ts @@ -0,0 +1,15 @@ +// SPDX-FileCopyrightText: 2024 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only +import { executeDefaultTextToTextExpression } from '../test-utils'; + +describe('The uppercase operator', () => { + it('should uppercase alphabetic characters successfully', async () => { + const result = await executeDefaultTextToTextExpression( + 'uppercase inputValue', + 'Hello, World!', + ); + + expect(result).toEqual('HELLO, WORLD!'); + }); +}); diff --git a/libs/language-server/src/lib/ast/expressions/evaluators/uppercase-operator-evaluator.ts b/libs/language-server/src/lib/ast/expressions/evaluators/uppercase-operator-evaluator.ts new file mode 100644 index 00000000..5b03e9ea --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/evaluators/uppercase-operator-evaluator.ts @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2024 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { DefaultUnaryOperatorEvaluator } from '../operator-evaluator'; +import { STRING_TYPEGUARD } from '../typeguards'; + +export class UppercaseOperatorEvaluator extends DefaultUnaryOperatorEvaluator< + string, + string +> { + constructor() { + super('uppercase', STRING_TYPEGUARD); + } + override doEvaluate(operandValue: string): string { + return operandValue.toUpperCase(); + } +} diff --git a/libs/language-server/src/lib/ast/expressions/evaluators/xor-operator-evaluator.ts b/libs/language-server/src/lib/ast/expressions/evaluators/xor-operator-evaluator.ts new file mode 100644 index 00000000..8531c3ac --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/evaluators/xor-operator-evaluator.ts @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type PolarsInternal } from '../internal-value-representation'; +import { DefaultBinaryOperatorEvaluator } from '../operator-evaluator'; +import { BOOLEAN_TYPEGUARD } from '../typeguards'; + +export class XorOperatorEvaluator extends DefaultBinaryOperatorEvaluator< + boolean, + boolean, + boolean +> { + constructor() { + super('xor', BOOLEAN_TYPEGUARD, BOOLEAN_TYPEGUARD); + } + override doEvaluate(leftValue: boolean, rightValue: boolean): boolean { + return (leftValue && !rightValue) || (!leftValue && rightValue); + } + override polarsDoEvaluate( + leftValue: PolarsInternal, + rightValue: PolarsInternal, + ): PolarsInternal { + // HACK: There should be an xor expression in polars + return leftValue.and(rightValue.not()).or(leftValue.not().and(rightValue)); + } +} diff --git a/libs/language-server/src/lib/ast/expressions/index.ts b/libs/language-server/src/lib/ast/expressions/index.ts new file mode 100644 index 00000000..3d1923df --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/index.ts @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +export * from './evaluation-strategy'; + +export * from './evaluation-context'; +export * from './internal-value-representation'; +export * from './operator-registry'; +export * from './type-inference'; +export * from './typeguards'; +export * from './evaluate-expression'; diff --git a/libs/language-server/src/lib/ast/expressions/internal-value-representation.ts b/libs/language-server/src/lib/ast/expressions/internal-value-representation.ts new file mode 100644 index 00000000..feea2188 --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/internal-value-representation.ts @@ -0,0 +1,97 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type pl } from 'nodejs-polars'; + +import { + type BlockTypeProperty, + type CellRangeLiteral, + type ConstraintDefinition, + type TransformDefinition, + type ValuetypeAssignment, + isBlockTypeProperty, + isCellRangeLiteral, + isConstraintDefinition, + isTransformDefinition, + isValuetypeAssignment, +} from '../generated/ast'; +import type { WrapperFactoryProvider } from '../wrappers'; + +export type PolarsInternal = pl.Expr; + +export type InternalValueRepresentation = + | AtomicInternalValueRepresentation + | InternalValueRepresentation[] + | []; + +export type AtomicInternalValueRepresentation = + | boolean + | number + | string + | RegExp + | CellRangeLiteral + | ConstraintDefinition + | ValuetypeAssignment + | BlockTypeProperty + | TransformDefinition; + +export type InternalValueRepresentationTypeguard< + T extends InternalValueRepresentation, +> = (value: unknown) => value is T; + +export function internalValueToString( + valueRepresentation: InternalValueRepresentation, + wrapperFactories: WrapperFactoryProvider, +): string { + if (Array.isArray(valueRepresentation)) { + return ( + '[ ' + + valueRepresentation + .map((item) => internalValueToString(item, wrapperFactories)) + .join(', ') + + ' ]' + ); + } + + if (typeof valueRepresentation === 'boolean') { + return String(valueRepresentation); + } + if (typeof valueRepresentation === 'number') { + if (valueRepresentation === Number.POSITIVE_INFINITY) { + return Number.MAX_VALUE.toLocaleString('fullwide', { + useGrouping: false, + }); + } + if (valueRepresentation === Number.NEGATIVE_INFINITY) { + return Number.MIN_VALUE.toLocaleString('fullwide', { + useGrouping: false, + }); + } + return `${valueRepresentation}`; + } + if (typeof valueRepresentation === 'string') { + return `"${valueRepresentation}"`; + } + if (valueRepresentation instanceof RegExp) { + return valueRepresentation.source; + } + if (isCellRangeLiteral(valueRepresentation)) { + return wrapperFactories.CellRange.wrap(valueRepresentation).toString(); + } + if (isConstraintDefinition(valueRepresentation)) { + return valueRepresentation.name; + } + if (isValuetypeAssignment(valueRepresentation)) { + return valueRepresentation.name; + } + if (isTransformDefinition(valueRepresentation)) { + return valueRepresentation.name; + } + if (isBlockTypeProperty(valueRepresentation)) { + return valueRepresentation.name; + } + throw new Error( + 'Convert of this InternalValueRepresentation is not implemented', + ); +} diff --git a/libs/language-server/src/lib/ast/expressions/operator-evaluator.ts b/libs/language-server/src/lib/ast/expressions/operator-evaluator.ts new file mode 100644 index 00000000..21251a07 --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/operator-evaluator.ts @@ -0,0 +1,441 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; + +import { type ValidationContext } from '../../validation/validation-context'; +import { + type BinaryExpression, + type TernaryExpression, + type UnaryExpression, +} from '../generated/ast'; +import { type WrapperFactoryProvider } from '../wrappers'; + +import { + evaluateExpression, + polarsEvaluateExpression, +} from './evaluate-expression'; +import { type EvaluationContext } from './evaluation-context'; +import { EvaluationStrategy } from './evaluation-strategy'; +import { + type InternalValueRepresentation, + type InternalValueRepresentationTypeguard, + type PolarsInternal, +} from './internal-value-representation'; +import { + type BinaryExpressionOperator, + type TernaryExpressionOperator, + type UnaryExpressionOperator, +} from './operator-types'; +import { + BOOLEAN_TYPEGUARD, + INTERNAL_VALUE_REPRESENTATION_TYPEGUARD, +} from './typeguards'; + +export interface OperatorEvaluator< + E extends UnaryExpression | BinaryExpression | TernaryExpression, +> { + readonly operator: E['operator']; + + /** + * @return the value the expression evaluates to or `undefined` if the evaluation failed. + */ + evaluate( + expression: E, + evaluationContext: EvaluationContext, + wrapperFactories: WrapperFactoryProvider, + strategy: EvaluationStrategy, + validationContext: ValidationContext | undefined, + ): InternalValueRepresentation | undefined; +} + +export interface PolarsOperatorEvaluator< + E extends UnaryExpression | BinaryExpression | TernaryExpression, +> extends OperatorEvaluator<E> { + readonly operator: E['operator']; + + polarsEvaluate( + expression: E, + evaluationContext: EvaluationContext, + wrapperFactories: WrapperFactoryProvider, + strategy: EvaluationStrategy, + validationContext: ValidationContext | undefined, + ): PolarsInternal | undefined; +} + +export abstract class DefaultUnaryOperatorEvaluator< + O extends InternalValueRepresentation, + T extends InternalValueRepresentation, +> implements PolarsOperatorEvaluator<UnaryExpression> +{ + constructor( + public readonly operator: UnaryExpressionOperator, + private readonly operandValueTypeguard: InternalValueRepresentationTypeguard<O>, + ) {} + polarsEvaluate( + expression: UnaryExpression, + evaluationContext: EvaluationContext, + wrapperFactories: WrapperFactoryProvider, + strategy: EvaluationStrategy, + validationContext: ValidationContext | undefined, + ): PolarsInternal | undefined { + assert(expression.operator === this.operator); + const operandValue = polarsEvaluateExpression( + expression.expression, + evaluationContext, + wrapperFactories, + validationContext, + strategy, + ); + if (operandValue === undefined) { + return undefined; + } + + return this.polarsDoEvaluate(operandValue, expression, validationContext); + } + + protected abstract doEvaluate( + operandValue: O, + expression: UnaryExpression, + context: ValidationContext | undefined, + ): T | undefined; + + protected polarsDoEvaluate( + _operand: PolarsInternal, + expression: UnaryExpression, + context: ValidationContext | undefined, + ): PolarsInternal | undefined { + context?.accept('error', `${expression.operator} is not supported yet.`, { + node: expression, + }); + return undefined; + } + + evaluate( + expression: UnaryExpression, + evaluationContext: EvaluationContext, + wrapperFactories: WrapperFactoryProvider, + strategy: EvaluationStrategy, + validationContext: ValidationContext | undefined, + ): T | undefined { + assert(expression.operator === this.operator); + const operandValue = evaluateExpression( + expression.expression, + evaluationContext, + wrapperFactories, + validationContext, + strategy, + ); + if (operandValue === undefined) { + return undefined; + } + + assert(this.operandValueTypeguard(operandValue)); + + return this.doEvaluate(operandValue, expression, validationContext); + } +} + +export abstract class DefaultBinaryOperatorEvaluator< + L extends InternalValueRepresentation, + R extends InternalValueRepresentation, + T extends InternalValueRepresentation, +> implements PolarsOperatorEvaluator<BinaryExpression> +{ + constructor( + public readonly operator: BinaryExpressionOperator, + private readonly leftValueTypeguard: InternalValueRepresentationTypeguard<L>, + private readonly rightValueTypeguard: InternalValueRepresentationTypeguard<R>, + ) {} + + protected abstract doEvaluate( + leftValue: L, + rightValue: R, + expression: BinaryExpression, + context: ValidationContext | undefined, + ): T | undefined; + + protected polarsDoEvaluate( + _left: PolarsInternal, + _right: PolarsInternal, + expression: BinaryExpression, + context: ValidationContext | undefined, + ): PolarsInternal | undefined { + context?.accept('error', `${expression.operator} is not supported yet.`, { + node: expression, + }); + return undefined; + } + + evaluate( + expression: BinaryExpression, + evaluationContext: EvaluationContext, + wrapperFactories: WrapperFactoryProvider, + strategy: EvaluationStrategy, + validationContext: ValidationContext | undefined, + ): T | undefined { + assert(expression.operator === this.operator); + const leftValue = evaluateExpression( + expression.left, + evaluationContext, + wrapperFactories, + validationContext, + strategy, + ); + if (strategy === EvaluationStrategy.LAZY && leftValue === undefined) { + return undefined; + } + const rightValue = evaluateExpression( + expression.right, + evaluationContext, + wrapperFactories, + validationContext, + strategy, + ); + if (leftValue === undefined || rightValue === undefined) { + return undefined; + } + + assert(this.leftValueTypeguard(leftValue)); + assert(this.rightValueTypeguard(rightValue)); + + return this.doEvaluate( + leftValue, + rightValue, + expression, + validationContext, + ); + } + + polarsEvaluate( + expression: BinaryExpression, + evaluationContext: EvaluationContext, + wrapperFactories: WrapperFactoryProvider, + strategy: EvaluationStrategy, + validationContext: ValidationContext | undefined, + ): PolarsInternal | undefined { + assert(expression.operator === this.operator); + const leftValue = polarsEvaluateExpression( + expression.left, + evaluationContext, + wrapperFactories, + validationContext, + strategy, + ); + if (strategy === EvaluationStrategy.LAZY && leftValue === undefined) { + return undefined; + } + const rightValue = polarsEvaluateExpression( + expression.right, + evaluationContext, + wrapperFactories, + validationContext, + strategy, + ); + if (leftValue === undefined || rightValue === undefined) { + return undefined; + } + + return this.polarsDoEvaluate( + leftValue, + rightValue, + expression, + validationContext, + ); + } +} + +/** + * This class serves as a base for boolean operators that support short-circuit evaluation. + * Short-circuit evaluation means that the right operand is not evaluated + * if the resulting value can be determined by solely evaluating the left operand. + */ +export abstract class BooleanShortCircuitOperatorEvaluator + implements PolarsOperatorEvaluator<BinaryExpression> +{ + constructor(public readonly operator: 'and' | 'or') {} + + protected abstract canSkipRightOperandEvaluation(leftValue: boolean): boolean; + protected abstract getShortCircuitValue(): boolean; + + protected abstract doEvaluate( + leftValue: boolean, + rightValue: boolean, + ): boolean; + + protected polarsDoEvaluate( + _left: PolarsInternal, + _right: PolarsInternal, + expression: BinaryExpression, + context: ValidationContext | undefined, + ): PolarsInternal | undefined { + context?.accept('error', `${expression.operator} is not supported yet.`, { + node: expression, + }); + return undefined; + } + + evaluate( + expression: BinaryExpression, + evaluationContext: EvaluationContext, + wrapperFactories: WrapperFactoryProvider, + strategy: EvaluationStrategy, + validationContext: ValidationContext | undefined, + ): boolean | undefined { + assert(expression.operator === this.operator); + const leftValue = evaluateExpression( + expression.left, + evaluationContext, + wrapperFactories, + validationContext, + strategy, + ); + assert(leftValue === undefined || typeof leftValue === 'boolean'); + if (strategy === EvaluationStrategy.LAZY) { + if (leftValue === undefined) { + return undefined; + } + if (this.canSkipRightOperandEvaluation(leftValue)) { + return this.getShortCircuitValue(); + } + } + + const rightValue = evaluateExpression( + expression.right, + evaluationContext, + wrapperFactories, + validationContext, + strategy, + ); + if (leftValue === undefined || rightValue === undefined) { + return undefined; + } + assert(typeof rightValue === 'boolean'); + + return this.doEvaluate(leftValue, rightValue); + } + + polarsEvaluate( + expression: BinaryExpression, + evaluationContext: EvaluationContext, + wrapperFactories: WrapperFactoryProvider, + strategy: EvaluationStrategy, + validationContext: ValidationContext | undefined, + ): PolarsInternal | undefined { + assert(expression.operator === this.operator); + const left = polarsEvaluateExpression( + expression.left, + evaluationContext, + wrapperFactories, + validationContext, + strategy, + ); + + if (strategy === EvaluationStrategy.LAZY && left === undefined) { + return undefined; + } + + const rightValue = polarsEvaluateExpression( + expression.right, + evaluationContext, + wrapperFactories, + validationContext, + strategy, + ); + if (left === undefined || rightValue === undefined) { + return undefined; + } + + return this.polarsDoEvaluate( + left, + rightValue, + expression, + validationContext, + ); + } +} + +export abstract class DefaultTernaryOperatorEvaluator< + FirstValue extends InternalValueRepresentation, + SecondValue extends InternalValueRepresentation, + ThirdValue extends InternalValueRepresentation, + ReturnValue extends InternalValueRepresentation, +> implements OperatorEvaluator<TernaryExpression> +{ + constructor( + public readonly operator: TernaryExpressionOperator, + private readonly firstValueTypeguard: InternalValueRepresentationTypeguard<FirstValue>, + private readonly secondValueTypeguard: InternalValueRepresentationTypeguard<SecondValue>, + private readonly thirdValueTypeguard: InternalValueRepresentationTypeguard<ThirdValue>, + ) {} + + protected abstract doEvaluate( + firstValue: FirstValue, + secondValue: SecondValue, + thirdValue: ThirdValue, + expression: TernaryExpression, + context: ValidationContext | undefined, + ): ReturnValue | undefined; + + evaluate( + expression: TernaryExpression, + evaluationContext: EvaluationContext, + wrapperFactories: WrapperFactoryProvider, + strategy: EvaluationStrategy, + validationContext: ValidationContext | undefined, + ): ReturnValue | undefined { + // The following linting exception can be removed when a second ternary operator is added + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + assert(expression.operator === this.operator); + + const firstValue = evaluateExpression( + expression.first, + evaluationContext, + wrapperFactories, + validationContext, + strategy, + ); + + if (strategy === EvaluationStrategy.LAZY && firstValue === undefined) { + return undefined; + } + + const secondValue = evaluateExpression( + expression.second, + evaluationContext, + wrapperFactories, + validationContext, + strategy, + ); + + const thirdValue = evaluateExpression( + expression.third, + evaluationContext, + wrapperFactories, + validationContext, + strategy, + ); + + if ( + firstValue === undefined || + secondValue === undefined || + thirdValue === undefined + ) { + return undefined; + } + + assert(this.firstValueTypeguard(firstValue)); + assert(this.secondValueTypeguard(secondValue)); + assert(this.thirdValueTypeguard(thirdValue)); + + return this.doEvaluate( + firstValue, + secondValue, + thirdValue, + expression, + validationContext, + ); + } +} diff --git a/libs/language-server/src/lib/ast/expressions/operator-registry.ts b/libs/language-server/src/lib/ast/expressions/operator-registry.ts new file mode 100644 index 00000000..bb4c4022 --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/operator-registry.ts @@ -0,0 +1,199 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type BinaryExpression, + type TernaryExpression, + type UnaryExpression, +} from '../generated/ast'; +import { + type ValueTypeProvider, + type WrapperFactoryProvider, +} from '../wrappers'; + +import { AdditionOperatorEvaluator } from './evaluators/addition-operator-evaluator'; +import { AndOperatorEvaluator } from './evaluators/and-operator-evaluator'; +import { CeilOperatorEvaluator } from './evaluators/ceil-operator-evaluator'; +import { DivisionOperatorEvaluator } from './evaluators/division-operator-evaluator'; +import { EqualityOperatorEvaluator } from './evaluators/equality-operator-evaluator'; +import { FloorOperatorEvaluator } from './evaluators/floor-operator-evaluator'; +import { GreaterEqualOperatorEvaluator } from './evaluators/greater-equal-operator-evaluator'; +import { GreaterThanOperatorEvaluator } from './evaluators/greater-than-operator-evaluator'; +import { InOperatorEvaluator } from './evaluators/in-operator-evaluator'; +import { InequalityOperatorEvaluator } from './evaluators/inequality-operator-evaluator'; +import { LessEqualOperatorEvaluator } from './evaluators/less-equal-operator-evaluator'; +import { LessThanOperatorEvaluator } from './evaluators/less-than-operator-evaluator'; +import { LowercaseOperatorEvaluator } from './evaluators/lowercase-operator-evaluator'; +import { MatchesOperatorEvaluator } from './evaluators/matches-operator-evaluator'; +import { MinusOperatorEvaluator } from './evaluators/minus-operator-evaluator'; +import { ModuloOperatorEvaluator } from './evaluators/modulo-operator-evaluator'; +import { MultiplicationOperatorEvaluator } from './evaluators/multiplication-operator-evaluator'; +import { NotOperatorEvaluator } from './evaluators/not-operator-evaluator'; +import { OrOperatorEvaluator } from './evaluators/or-operator-evaluator'; +import { + AsBooleanOperatorEvaluator, + AsDecimalOperatorEvaluator, + AsIntegerOperatorEvaluator, + AsTextOperatorEvaluator, +} from './evaluators/parse-operators-evaluators'; +import { PlusOperatorEvaluator } from './evaluators/plus-operator-evaluator'; +import { PowOperatorEvaluator } from './evaluators/pow-operator-evaluator'; +import { ReplaceOperatorEvaluator } from './evaluators/replace-operator-evaluator'; +import { RootOperatorEvaluator } from './evaluators/root-operator-evaluator'; +import { RoundOperatorEvaluator } from './evaluators/round-operator-evaluator'; +import { SqrtOperatorEvaluator } from './evaluators/sqrt-operator-evaluator'; +import { SubtractionOperatorEvaluator } from './evaluators/subtraction-operator-evaluator'; +import { UppercaseOperatorEvaluator } from './evaluators/uppercase-operator-evaluator'; +import { XorOperatorEvaluator } from './evaluators/xor-operator-evaluator'; +import { + type OperatorEvaluator, + type PolarsOperatorEvaluator, +} from './operator-evaluator'; +import { + type BinaryOperatorTypeComputer, + type TernaryOperatorTypeComputer, + type UnaryOperatorTypeComputer, +} from './operator-type-computer'; +import { + type BinaryExpressionOperator, + type TernaryExpressionOperator, + type UnaryExpressionOperator, +} from './operator-types'; +import { BasicArithmeticOperatorTypeComputer } from './type-computers/basic-arithmetic-operator-type-computer'; +import { DivisionOperatorTypeComputer } from './type-computers/division-operator-type-computer'; +import { EqualityOperatorTypeComputer } from './type-computers/equality-operator-type-computer'; +import { ExponentialOperatorTypeComputer } from './type-computers/exponential-operator-type-computer'; +import { InOperatorTypeComputer } from './type-computers/in-operator-type-computer'; +import { IntegerConversionOperatorTypeComputer } from './type-computers/integer-conversion-operator-type-computer'; +import { LogicalOperatorTypeComputer } from './type-computers/logical-operator-type-computer'; +import { MatchesOperatorTypeComputer } from './type-computers/matches-operator-type-computer'; +import { NotOperatorTypeComputer } from './type-computers/not-operator-type-computer'; +import { + AsBooleanOperatorTypeComputer, + AsDecimalOperatorTypeComputer, + AsIntegerOperatorTypeComputer, + AsTextOperatorTypeComputer, +} from './type-computers/parse-opterators-type-computer'; +import { RelationalOperatorTypeComputer } from './type-computers/relational-operator-type-computer'; +import { ReplaceOperatorTypeComputer } from './type-computers/replace-operator-type-computer'; +import { SignOperatorTypeComputer } from './type-computers/sign-operator-type-computer'; +import { SqrtOperatorTypeComputer } from './type-computers/sqrt-operator-type-computer'; +import { StringTransformTypeComputer } from './type-computers/string-transform-type-computer'; + +export interface OperatorEvaluatorRegistry { + unary: Record< + UnaryExpressionOperator, + PolarsOperatorEvaluator<UnaryExpression> + >; + binary: Record< + BinaryExpressionOperator, + PolarsOperatorEvaluator<BinaryExpression> + >; + ternary: Record< + TernaryExpressionOperator, + OperatorEvaluator<TernaryExpression> + >; +} + +export interface OperatorTypeComputerRegistry { + unary: Record<UnaryExpressionOperator, UnaryOperatorTypeComputer>; + binary: Record<BinaryExpressionOperator, BinaryOperatorTypeComputer>; + ternary: Record<TernaryExpressionOperator, TernaryOperatorTypeComputer>; +} + +export class DefaultOperatorEvaluatorRegistry + implements OperatorEvaluatorRegistry +{ + unary = { + not: new NotOperatorEvaluator(), + '+': new PlusOperatorEvaluator(), + '-': new MinusOperatorEvaluator(), + sqrt: new SqrtOperatorEvaluator(), + floor: new FloorOperatorEvaluator(), + ceil: new CeilOperatorEvaluator(), + round: new RoundOperatorEvaluator(), + lowercase: new LowercaseOperatorEvaluator(), + uppercase: new UppercaseOperatorEvaluator(), + asDecimal: new AsDecimalOperatorEvaluator(this.valueTypeProvider), + asInteger: new AsIntegerOperatorEvaluator(this.valueTypeProvider), + asBoolean: new AsBooleanOperatorEvaluator(this.valueTypeProvider), + asText: new AsTextOperatorEvaluator(this.valueTypeProvider), + }; + binary = { + pow: new PowOperatorEvaluator(), + root: new RootOperatorEvaluator(), + '*': new MultiplicationOperatorEvaluator(), + '/': new DivisionOperatorEvaluator(), + '%': new ModuloOperatorEvaluator(), + '+': new AdditionOperatorEvaluator(), + '-': new SubtractionOperatorEvaluator(), + matches: new MatchesOperatorEvaluator(), + in: new InOperatorEvaluator(), + '<': new LessThanOperatorEvaluator(), + '<=': new LessEqualOperatorEvaluator(), + '>': new GreaterThanOperatorEvaluator(), + '>=': new GreaterEqualOperatorEvaluator(), + '==': new EqualityOperatorEvaluator(), + '!=': new InequalityOperatorEvaluator(), + xor: new XorOperatorEvaluator(), + and: new AndOperatorEvaluator(), + or: new OrOperatorEvaluator(), + }; + ternary = { + replace: new ReplaceOperatorEvaluator(), + }; + + constructor(private readonly valueTypeProvider: ValueTypeProvider) {} +} + +export class DefaultOperatorTypeComputerRegistry + implements OperatorTypeComputerRegistry +{ + unary = { + not: new NotOperatorTypeComputer(this.valueTypeProvider), + '+': new SignOperatorTypeComputer(this.valueTypeProvider), + '-': new SignOperatorTypeComputer(this.valueTypeProvider), + sqrt: new SqrtOperatorTypeComputer(this.valueTypeProvider), + floor: new IntegerConversionOperatorTypeComputer(this.valueTypeProvider), + ceil: new IntegerConversionOperatorTypeComputer(this.valueTypeProvider), + round: new IntegerConversionOperatorTypeComputer(this.valueTypeProvider), + lowercase: new StringTransformTypeComputer(this.valueTypeProvider), + uppercase: new StringTransformTypeComputer(this.valueTypeProvider), + asDecimal: new AsDecimalOperatorTypeComputer(this.valueTypeProvider), + asInteger: new AsIntegerOperatorTypeComputer(this.valueTypeProvider), + asBoolean: new AsBooleanOperatorTypeComputer(this.valueTypeProvider), + asText: new AsTextOperatorTypeComputer(this.valueTypeProvider), + }; + binary = { + pow: new ExponentialOperatorTypeComputer(this.valueTypeProvider), + root: new ExponentialOperatorTypeComputer(this.valueTypeProvider), + '*': new BasicArithmeticOperatorTypeComputer(this.valueTypeProvider), + '/': new DivisionOperatorTypeComputer(this.valueTypeProvider), + '%': new DivisionOperatorTypeComputer(this.valueTypeProvider), + '+': new BasicArithmeticOperatorTypeComputer(this.valueTypeProvider), + '-': new BasicArithmeticOperatorTypeComputer(this.valueTypeProvider), + matches: new MatchesOperatorTypeComputer(this.valueTypeProvider), + in: new InOperatorTypeComputer( + this.valueTypeProvider, + this.wrapperFactories, + ), + '<': new RelationalOperatorTypeComputer(this.valueTypeProvider), + '<=': new RelationalOperatorTypeComputer(this.valueTypeProvider), + '>': new RelationalOperatorTypeComputer(this.valueTypeProvider), + '>=': new RelationalOperatorTypeComputer(this.valueTypeProvider), + '==': new EqualityOperatorTypeComputer(this.valueTypeProvider), + '!=': new EqualityOperatorTypeComputer(this.valueTypeProvider), + xor: new LogicalOperatorTypeComputer(this.valueTypeProvider), + and: new LogicalOperatorTypeComputer(this.valueTypeProvider), + or: new LogicalOperatorTypeComputer(this.valueTypeProvider), + }; + ternary = { + replace: new ReplaceOperatorTypeComputer(this.valueTypeProvider), + }; + + constructor( + private readonly valueTypeProvider: ValueTypeProvider, + private readonly wrapperFactories: WrapperFactoryProvider, + ) {} +} diff --git a/libs/language-server/src/lib/ast/expressions/operator-type-computer.ts b/libs/language-server/src/lib/ast/expressions/operator-type-computer.ts new file mode 100644 index 00000000..2a21d584 --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/operator-type-computer.ts @@ -0,0 +1,230 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type ValidationContext } from '../../validation/validation-context'; +import { + type BinaryExpression, + type TernaryExpression, + type UnaryExpression, +} from '../generated/ast'; +import { type ValueType } from '../wrappers/value-type/value-type'; + +export interface UnaryOperatorTypeComputer { + /** + * Computes the type of a unary operator by the type of its operand. + * @param operandType the type of the operand + * @param expression the expression to use for diagnostics + * @param context the validation context to use for diagnostics + * @returns the resulting type of the operator or `undefined` if the type could not be inferred + */ + computeType( + operandType: ValueType, + expression: UnaryExpression, + context: ValidationContext | undefined, + ): ValueType | undefined; +} + +export abstract class DefaultUnaryOperatorTypeComputer + implements UnaryOperatorTypeComputer +{ + constructor(protected readonly expectedOperandType: ValueType) {} + + computeType( + operandType: ValueType, + expression: UnaryExpression, + context: ValidationContext | undefined, + ): ValueType | undefined { + if (!operandType.isConvertibleTo(this.expectedOperandType)) { + context?.accept( + 'error', + generateUnexpectedTypeMessage(this.expectedOperandType, operandType), + { + node: expression.expression, + }, + ); + return undefined; + } + return this.doComputeType(operandType); + } + + protected abstract doComputeType(operandType: ValueType): ValueType; +} + +export interface BinaryOperatorTypeComputer { + /** + * Computes the type of a binary operator by the type of its operands. + * @param leftType the type of the left operand + * @param rightType the type of the right operand + * @param expression the expression to use for diagnostics + * @param context the validation context to use for diagnostics + * @returns the resulting type of the operator or `undefined` if the type could not be inferred + */ + computeType( + leftType: ValueType, + rightType: ValueType, + expression: BinaryExpression, + context: ValidationContext | undefined, + ): ValueType | undefined; +} + +export abstract class DefaultBinaryOperatorTypeComputer + implements BinaryOperatorTypeComputer +{ + constructor( + protected readonly expectedLeftOperandType: ValueType, + protected readonly expectedRightOperandType: ValueType, + ) {} + + computeType( + leftOperandType: ValueType, + rightOperandType: ValueType, + expression: BinaryExpression, + context: ValidationContext | undefined, + ): ValueType | undefined { + let typeErrorOccurred = false; + + if (!leftOperandType.isConvertibleTo(this.expectedLeftOperandType)) { + context?.accept( + 'error', + generateUnexpectedTypeMessage( + this.expectedLeftOperandType, + leftOperandType, + ), + { + node: expression.left, + }, + ); + typeErrorOccurred = true; + } + + if (!rightOperandType.isConvertibleTo(this.expectedRightOperandType)) { + context?.accept( + 'error', + generateUnexpectedTypeMessage( + this.expectedRightOperandType, + rightOperandType, + ), + { + node: expression.right, + }, + ); + typeErrorOccurred = true; + } + + if (typeErrorOccurred) { + return undefined; + } + + return this.doComputeType(leftOperandType, rightOperandType); + } + + protected abstract doComputeType( + leftOperandType: ValueType, + rightOperandType: ValueType, + ): ValueType; +} + +export function generateUnexpectedTypeMessage( + expectedType: ValueType, + actualType: ValueType, +) { + return `The operand needs to be of type ${expectedType.getName()} but is of type ${actualType.getName()}`; +} + +export abstract class DefaultTernaryOperatorTypeComputer + implements TernaryOperatorTypeComputer +{ + constructor( + protected readonly expectedFirstOperandType: ValueType, + protected readonly expectedSecondOperandType: ValueType, + protected readonly expectedThirdOperandType: ValueType, + ) {} + + computeType( + firstOperandType: ValueType, + secondOperandType: ValueType, + thirdOperandType: ValueType, + expression: TernaryExpression, + context: ValidationContext | undefined, + ): ValueType | undefined { + let typeErrorOccurred = false; + + if (!firstOperandType.isConvertibleTo(this.expectedFirstOperandType)) { + context?.accept( + 'error', + generateUnexpectedTypeMessage( + this.expectedFirstOperandType, + firstOperandType, + ), + { + node: expression.first, + }, + ); + typeErrorOccurred = true; + } + + if (!secondOperandType.isConvertibleTo(this.expectedSecondOperandType)) { + context?.accept( + 'error', + generateUnexpectedTypeMessage( + this.expectedSecondOperandType, + secondOperandType, + ), + { + node: expression.second, + }, + ); + typeErrorOccurred = true; + } + + if (!thirdOperandType.isConvertibleTo(this.expectedThirdOperandType)) { + context?.accept( + 'error', + generateUnexpectedTypeMessage( + this.expectedThirdOperandType, + thirdOperandType, + ), + { + node: expression.third, + }, + ); + typeErrorOccurred = true; + } + + if (typeErrorOccurred) { + return undefined; + } + + return this.doComputeType( + firstOperandType, + secondOperandType, + thirdOperandType, + ); + } + + protected abstract doComputeType( + firstOperandType: ValueType, + secondOperandType: ValueType, + thirdOperandType: ValueType, + ): ValueType; +} + +export interface TernaryOperatorTypeComputer { + /** + * Computes the type of a ternary operator by the type of its operands. + * @param firstType the type of the first operand + * @param secondType the type of the second operand + * @param thirdType the type of the third operand + * @param expression the expression to use for diagnostics + * @param context the validation context to use for diagnostics + * @returns the resulting type of the operator or `undefined` if the type could not be inferred + */ + computeType( + firstType: ValueType, + secondType: ValueType, + thirdType: ValueType, + expression: TernaryExpression, + context: ValidationContext | undefined, + ): ValueType | undefined; +} diff --git a/libs/language-server/src/lib/ast/expressions/operator-types.ts b/libs/language-server/src/lib/ast/expressions/operator-types.ts new file mode 100644 index 00000000..84f288f9 --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/operator-types.ts @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type BinaryExpression, + type TernaryExpression, + type UnaryExpression, +} from '../generated/ast'; + +export type UnaryExpressionOperator = UnaryExpression['operator']; +export type BinaryExpressionOperator = BinaryExpression['operator']; +export type TernaryExpressionOperator = TernaryExpression['operator']; diff --git a/libs/language-server/src/lib/ast/expressions/test-utils.ts b/libs/language-server/src/lib/ast/expressions/test-utils.ts new file mode 100644 index 00000000..bc6eff62 --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/test-utils.ts @@ -0,0 +1,67 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { NodeFileSystem } from 'langium/node'; + +import { parseHelper } from '../../../test/langium-utils'; +import { createJayveeServices } from '../../jayvee-module'; +import { type TransformDefinition } from '../generated/ast'; + +import { evaluateExpression } from './evaluate-expression'; +import { EvaluationContext } from './evaluation-context'; +import { type InternalValueRepresentation } from './internal-value-representation'; + +export async function executeDefaultTextToTextExpression( + expression: string, + input: InternalValueRepresentation, +) { + return executeExpressionTestHelper( + expression, + 'inputValue', + 'text', + input, + 'text', + ); +} + +export async function executeExpressionTestHelper( + expression: string, + inputValueName: string, + inputValueType: 'text', + inputValueValue: InternalValueRepresentation, + outputValueType: 'text', +): Promise<InternalValueRepresentation | undefined> { + const services = createJayveeServices(NodeFileSystem).Jayvee; + const parse = parseHelper(services); + const locator = services.workspace.AstNodeLocator; + + const document = await parse(` + transform TestTransform { + from ${inputValueName} oftype ${inputValueType}; + to outputValue oftype ${outputValueType}; + + outputValue: ${expression}; + } + `); + + const transform = locator.getAstNode<TransformDefinition>( + document.parseResult.value, + 'transforms@0', + ) as TransformDefinition; + + const evaluationContext = new EvaluationContext( + services.RuntimeParameterProvider, + services.operators.EvaluatorRegistry, + services.ValueTypeProvider, + ); + + evaluationContext.setValueForReference(inputValueName, inputValueValue); + + return evaluateExpression( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + transform.body.outputAssignments[0]!.expression, + evaluationContext, + services.WrapperFactories, + ); +} diff --git a/libs/language-server/src/lib/ast/expressions/type-computers/basic-arithmetic-operator-type-computer.ts b/libs/language-server/src/lib/ast/expressions/type-computers/basic-arithmetic-operator-type-computer.ts new file mode 100644 index 00000000..50da9b55 --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/type-computers/basic-arithmetic-operator-type-computer.ts @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type ValueType, + type ValueTypeProvider, +} from '../../wrappers/value-type'; +import { DefaultBinaryOperatorTypeComputer } from '../operator-type-computer'; + +export class BasicArithmeticOperatorTypeComputer extends DefaultBinaryOperatorTypeComputer { + constructor(protected readonly valueTypeProvider: ValueTypeProvider) { + super( + valueTypeProvider.Primitives.Decimal, + valueTypeProvider.Primitives.Decimal, + ); + } + + override doComputeType( + leftOperandType: ValueType, + rightOperandType: ValueType, + ): ValueType { + if ( + leftOperandType === this.valueTypeProvider.Primitives.Integer && + rightOperandType === this.valueTypeProvider.Primitives.Integer + ) { + return this.valueTypeProvider.Primitives.Integer; + } + return this.valueTypeProvider.Primitives.Decimal; + } +} diff --git a/libs/language-server/src/lib/ast/expressions/type-computers/division-operator-type-computer.ts b/libs/language-server/src/lib/ast/expressions/type-computers/division-operator-type-computer.ts new file mode 100644 index 00000000..556b8797 --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/type-computers/division-operator-type-computer.ts @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type ValueType, + type ValueTypeProvider, +} from '../../wrappers/value-type'; +import { DefaultBinaryOperatorTypeComputer } from '../operator-type-computer'; + +export class DivisionOperatorTypeComputer extends DefaultBinaryOperatorTypeComputer { + constructor(protected readonly valueTypeProvider: ValueTypeProvider) { + super( + valueTypeProvider.Primitives.Decimal, + valueTypeProvider.Primitives.Decimal, + ); + } + + override doComputeType(): ValueType { + return this.valueTypeProvider.Primitives.Decimal; + } +} diff --git a/libs/language-server/src/lib/ast/expressions/type-computers/equality-operator-type-computer.ts b/libs/language-server/src/lib/ast/expressions/type-computers/equality-operator-type-computer.ts new file mode 100644 index 00000000..b185d960 --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/type-computers/equality-operator-type-computer.ts @@ -0,0 +1,73 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type ValidationContext } from '../../../validation/validation-context'; +import { type BinaryExpression } from '../../generated/ast'; +import { + type ValueType, + type ValueTypeProvider, +} from '../../wrappers/value-type'; +import { type BinaryOperatorTypeComputer } from '../operator-type-computer'; + +export class EqualityOperatorTypeComputer + implements BinaryOperatorTypeComputer +{ + private readonly ALLOWED_OPERAND_TYPES: ValueType[]; + + constructor(protected readonly valueTypeProvider: ValueTypeProvider) { + this.ALLOWED_OPERAND_TYPES = [ + valueTypeProvider.Primitives.Boolean, + valueTypeProvider.Primitives.Text, + valueTypeProvider.Primitives.Integer, + valueTypeProvider.Primitives.Decimal, + ]; + } + + computeType( + leftOperandType: ValueType, + rightOperandType: ValueType, + expression: BinaryExpression, + context: ValidationContext | undefined, + ): ValueType | undefined { + const isLeftOperandTypeValid = + this.ALLOWED_OPERAND_TYPES.includes(leftOperandType); + const isRightOperandTypeValid = + this.ALLOWED_OPERAND_TYPES.includes(rightOperandType); + if (!isLeftOperandTypeValid || !isRightOperandTypeValid) { + if (!isLeftOperandTypeValid) { + context?.accept( + 'error', + `Operator does not support type ${leftOperandType.getName()}`, + { + node: expression.left, + }, + ); + } + if (!isRightOperandTypeValid) { + context?.accept( + 'error', + `Operator does not support type ${rightOperandType.getName()}`, + { + node: expression.right, + }, + ); + } + return undefined; + } + + if ( + !leftOperandType.isConvertibleTo(rightOperandType) && + !rightOperandType.isConvertibleTo(leftOperandType) + ) { + context?.accept( + 'error', + `The types of the operands need to be equal but they differ (left: ${leftOperandType.getName()}, right: ${rightOperandType.getName()})`, + { node: expression }, + ); + return undefined; + } + + return this.valueTypeProvider.Primitives.Boolean; + } +} diff --git a/libs/language-server/src/lib/ast/expressions/type-computers/exponential-operator-type-computer.ts b/libs/language-server/src/lib/ast/expressions/type-computers/exponential-operator-type-computer.ts new file mode 100644 index 00000000..f840fe8f --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/type-computers/exponential-operator-type-computer.ts @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type ValueType, + type ValueTypeProvider, +} from '../../wrappers/value-type'; +import { DefaultBinaryOperatorTypeComputer } from '../operator-type-computer'; + +export class ExponentialOperatorTypeComputer extends DefaultBinaryOperatorTypeComputer { + constructor(protected readonly valueTypeProvider: ValueTypeProvider) { + super( + valueTypeProvider.Primitives.Decimal, + valueTypeProvider.Primitives.Decimal, + ); + } + + protected override doComputeType(): ValueType { + return this.valueTypeProvider.Primitives.Decimal; + } +} diff --git a/libs/language-server/src/lib/ast/expressions/type-computers/in-operator-type-computer.ts b/libs/language-server/src/lib/ast/expressions/type-computers/in-operator-type-computer.ts new file mode 100644 index 00000000..7a7b7e09 --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/type-computers/in-operator-type-computer.ts @@ -0,0 +1,104 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; + +import { type ValidationContext } from '../../../validation/validation-context'; +import { type BinaryExpression } from '../../generated/ast'; +import { type WrapperFactoryProvider } from '../../wrappers'; +import { + type CollectionValueType, + type ValueType, + type ValueTypeProvider, + isCollectionValueType, +} from '../../wrappers/value-type'; +import { type BinaryOperatorTypeComputer } from '../operator-type-computer'; + +export class InOperatorTypeComputer implements BinaryOperatorTypeComputer { + private readonly ALLOWED_LEFT_OPERAND_TYPES: ValueType[]; + private readonly ALLOWED_RIGHT_OPERAND_TYPES: CollectionValueType[]; + + constructor( + protected readonly valueTypeProvider: ValueTypeProvider, + protected readonly wrapperFactories: WrapperFactoryProvider, + ) { + this.ALLOWED_LEFT_OPERAND_TYPES = [ + valueTypeProvider.Primitives.Text, + valueTypeProvider.Primitives.Integer, + valueTypeProvider.Primitives.Decimal, + ]; + this.ALLOWED_RIGHT_OPERAND_TYPES = this.ALLOWED_LEFT_OPERAND_TYPES.map( + (v) => valueTypeProvider.createCollectionValueTypeOf(v), + ); + } + + computeType( + leftOperandType: ValueType, + rightOperandType: ValueType, + expression: BinaryExpression, + context: ValidationContext | undefined, + ): ValueType | undefined { + const isLeftOperandTypeValid = + this.ALLOWED_LEFT_OPERAND_TYPES.includes(leftOperandType); + const isRightOperandTypeValid = this.ALLOWED_RIGHT_OPERAND_TYPES.some((v) => + v.equals(rightOperandType), + ); + if (!isLeftOperandTypeValid || !isRightOperandTypeValid) { + if (!isLeftOperandTypeValid) { + context?.accept( + 'error', + `Operator does not support type ${leftOperandType.getName()}`, + { + node: expression.left, + }, + ); + } + if (!isRightOperandTypeValid) { + context?.accept( + 'error', + `Operator does not support type ${rightOperandType.getName()}`, + { + node: expression.right, + }, + ); + } + return undefined; + } + assert( + isCollectionValueType( + rightOperandType, + this.valueTypeProvider.Primitives.Decimal, + ) || + isCollectionValueType( + rightOperandType, + this.valueTypeProvider.Primitives.Integer, + ) || + isCollectionValueType( + rightOperandType, + this.valueTypeProvider.Primitives.Text, + ), + ); + + // allow 3 in [3.5, 5.3] + const isLeftConvertibleToRight = leftOperandType.isConvertibleTo( + rightOperandType.elementType, + ); + + // allow 3.5 in [3.0, 2.0] + const isRightConvertibleToLeft = + rightOperandType.elementType.isConvertibleTo(leftOperandType); + + if (!isLeftConvertibleToRight && !isRightConvertibleToLeft) { + context?.accept( + 'error', + `The type of the left operand needs to be compatible to the collection element type of the right operand but they differ (left: ${leftOperandType.getName()}, right: ${rightOperandType.elementType.getName()})`, + { node: expression }, + ); + return undefined; + } + + return this.valueTypeProvider.Primitives.Boolean; + } +} diff --git a/libs/language-server/src/lib/ast/expressions/type-computers/integer-conversion-operator-type-computer.ts b/libs/language-server/src/lib/ast/expressions/type-computers/integer-conversion-operator-type-computer.ts new file mode 100644 index 00000000..dffe6438 --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/type-computers/integer-conversion-operator-type-computer.ts @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type ValueType, + type ValueTypeProvider, +} from '../../wrappers/value-type'; +import { DefaultUnaryOperatorTypeComputer } from '../operator-type-computer'; + +export class IntegerConversionOperatorTypeComputer extends DefaultUnaryOperatorTypeComputer { + constructor(protected readonly valueTypeProvider: ValueTypeProvider) { + super(valueTypeProvider.Primitives.Decimal); + } + + override doComputeType(): ValueType { + return this.valueTypeProvider.Primitives.Integer; + } +} diff --git a/libs/language-server/src/lib/ast/expressions/type-computers/logical-operator-type-computer.ts b/libs/language-server/src/lib/ast/expressions/type-computers/logical-operator-type-computer.ts new file mode 100644 index 00000000..eed1e5a1 --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/type-computers/logical-operator-type-computer.ts @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type ValueType, + type ValueTypeProvider, +} from '../../wrappers/value-type'; +import { DefaultBinaryOperatorTypeComputer } from '../operator-type-computer'; + +export class LogicalOperatorTypeComputer extends DefaultBinaryOperatorTypeComputer { + constructor(protected readonly valueTypeProvider: ValueTypeProvider) { + super( + valueTypeProvider.Primitives.Boolean, + valueTypeProvider.Primitives.Boolean, + ); + } + + protected override doComputeType(): ValueType { + return this.valueTypeProvider.Primitives.Boolean; + } +} diff --git a/libs/language-server/src/lib/ast/expressions/type-computers/matches-operator-type-computer.ts b/libs/language-server/src/lib/ast/expressions/type-computers/matches-operator-type-computer.ts new file mode 100644 index 00000000..2780b05e --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/type-computers/matches-operator-type-computer.ts @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type ValueType, + type ValueTypeProvider, +} from '../../wrappers/value-type'; +import { DefaultBinaryOperatorTypeComputer } from '../operator-type-computer'; + +export class MatchesOperatorTypeComputer extends DefaultBinaryOperatorTypeComputer { + constructor(protected readonly valueTypeProvider: ValueTypeProvider) { + super( + valueTypeProvider.Primitives.Text, + valueTypeProvider.Primitives.Regex, + ); + } + + override doComputeType(): ValueType { + return this.valueTypeProvider.Primitives.Boolean; + } +} diff --git a/libs/language-server/src/lib/ast/expressions/type-computers/not-operator-type-computer.ts b/libs/language-server/src/lib/ast/expressions/type-computers/not-operator-type-computer.ts new file mode 100644 index 00000000..543946dc --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/type-computers/not-operator-type-computer.ts @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type ValueType, + type ValueTypeProvider, +} from '../../wrappers/value-type'; +import { DefaultUnaryOperatorTypeComputer } from '../operator-type-computer'; + +export class NotOperatorTypeComputer extends DefaultUnaryOperatorTypeComputer { + constructor(protected readonly valueTypeProvider: ValueTypeProvider) { + super(valueTypeProvider.Primitives.Boolean); + } + + override doComputeType(): ValueType { + return this.valueTypeProvider.Primitives.Boolean; + } +} diff --git a/libs/language-server/src/lib/ast/expressions/type-computers/parse-opterators-type-computer.ts b/libs/language-server/src/lib/ast/expressions/type-computers/parse-opterators-type-computer.ts new file mode 100644 index 00000000..7cac652a --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/type-computers/parse-opterators-type-computer.ts @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: 2024 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type ValueType, + type ValueTypeProvider, +} from '../../wrappers/value-type'; +import { DefaultUnaryOperatorTypeComputer } from '../operator-type-computer'; + +export class AsDecimalOperatorTypeComputer extends DefaultUnaryOperatorTypeComputer { + constructor(protected readonly valueTypeProvider: ValueTypeProvider) { + super(valueTypeProvider.Primitives.Text); + } + + override doComputeType(): ValueType { + return this.valueTypeProvider.Primitives.Decimal; + } +} + +export class AsTextOperatorTypeComputer extends DefaultUnaryOperatorTypeComputer { + constructor(protected readonly valueTypeProvider: ValueTypeProvider) { + super(valueTypeProvider.Primitives.Text); + } + + override doComputeType(): ValueType { + return this.valueTypeProvider.Primitives.Text; + } +} + +export class AsIntegerOperatorTypeComputer extends DefaultUnaryOperatorTypeComputer { + constructor(protected readonly valueTypeProvider: ValueTypeProvider) { + super(valueTypeProvider.Primitives.Text); + } + + override doComputeType(): ValueType { + return this.valueTypeProvider.Primitives.Integer; + } +} + +export class AsBooleanOperatorTypeComputer extends DefaultUnaryOperatorTypeComputer { + constructor(protected readonly valueTypeProvider: ValueTypeProvider) { + super(valueTypeProvider.Primitives.Text); + } + + override doComputeType(): ValueType { + return this.valueTypeProvider.Primitives.Boolean; + } +} diff --git a/libs/language-server/src/lib/ast/expressions/type-computers/relational-operator-type-computer.ts b/libs/language-server/src/lib/ast/expressions/type-computers/relational-operator-type-computer.ts new file mode 100644 index 00000000..18ae68bc --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/type-computers/relational-operator-type-computer.ts @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type ValueType, + type ValueTypeProvider, +} from '../../wrappers/value-type'; +import { DefaultBinaryOperatorTypeComputer } from '../operator-type-computer'; + +export class RelationalOperatorTypeComputer extends DefaultBinaryOperatorTypeComputer { + constructor(protected readonly valueTypeProvider: ValueTypeProvider) { + super( + valueTypeProvider.Primitives.Decimal, + valueTypeProvider.Primitives.Decimal, + ); + } + + override doComputeType(): ValueType { + return this.valueTypeProvider.Primitives.Boolean; + } +} diff --git a/libs/language-server/src/lib/ast/expressions/type-computers/replace-operator-type-computer.ts b/libs/language-server/src/lib/ast/expressions/type-computers/replace-operator-type-computer.ts new file mode 100644 index 00000000..d6c3584b --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/type-computers/replace-operator-type-computer.ts @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type ValueType, + type ValueTypeProvider, +} from '../../wrappers/value-type'; +import { DefaultTernaryOperatorTypeComputer } from '../operator-type-computer'; + +export class ReplaceOperatorTypeComputer extends DefaultTernaryOperatorTypeComputer { + constructor(protected readonly valueTypeProvider: ValueTypeProvider) { + super( + valueTypeProvider.Primitives.Text, + valueTypeProvider.Primitives.Regex, + valueTypeProvider.Primitives.Text, + ); + } + + override doComputeType(): ValueType { + return this.valueTypeProvider.Primitives.Text; + } +} diff --git a/libs/language-server/src/lib/ast/expressions/type-computers/sign-operator-type-computer.ts b/libs/language-server/src/lib/ast/expressions/type-computers/sign-operator-type-computer.ts new file mode 100644 index 00000000..b6e16cd5 --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/type-computers/sign-operator-type-computer.ts @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type ValueType, + type ValueTypeProvider, +} from '../../wrappers/value-type'; +import { DefaultUnaryOperatorTypeComputer } from '../operator-type-computer'; + +export class SignOperatorTypeComputer extends DefaultUnaryOperatorTypeComputer { + constructor(protected readonly valueTypeProvider: ValueTypeProvider) { + super(valueTypeProvider.Primitives.Decimal); + } + + override doComputeType(operandType: ValueType): ValueType { + return operandType; + } +} diff --git a/libs/language-server/src/lib/ast/expressions/type-computers/sqrt-operator-type-computer.ts b/libs/language-server/src/lib/ast/expressions/type-computers/sqrt-operator-type-computer.ts new file mode 100644 index 00000000..ac913254 --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/type-computers/sqrt-operator-type-computer.ts @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type ValueType, + type ValueTypeProvider, +} from '../../wrappers/value-type'; +import { DefaultUnaryOperatorTypeComputer } from '../operator-type-computer'; + +export class SqrtOperatorTypeComputer extends DefaultUnaryOperatorTypeComputer { + constructor(protected readonly valueTypeProvider: ValueTypeProvider) { + super(valueTypeProvider.Primitives.Decimal); + } + + override doComputeType(): ValueType { + return this.valueTypeProvider.Primitives.Decimal; + } +} diff --git a/libs/language-server/src/lib/ast/expressions/type-computers/string-transform-type-computer.ts b/libs/language-server/src/lib/ast/expressions/type-computers/string-transform-type-computer.ts new file mode 100644 index 00000000..e27aa696 --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/type-computers/string-transform-type-computer.ts @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: 2024 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type ValueType, + type ValueTypeProvider, +} from '../../wrappers/value-type'; +import { DefaultUnaryOperatorTypeComputer } from '../operator-type-computer'; + +export class StringTransformTypeComputer extends DefaultUnaryOperatorTypeComputer { + constructor(protected readonly valueTypeProvider: ValueTypeProvider) { + super(valueTypeProvider.Primitives.Text); + } + + override doComputeType(): ValueType { + return this.valueTypeProvider.Primitives.Text; + } +} diff --git a/libs/language-server/src/lib/ast/expressions/type-inference.ts b/libs/language-server/src/lib/ast/expressions/type-inference.ts new file mode 100644 index 00000000..6580a5a4 --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/type-inference.ts @@ -0,0 +1,365 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { assertUnreachable } from 'langium'; + +import { type ValidationContext } from '../../validation/validation-context'; +import { + type CollectionLiteral, + type Expression, + type ExpressionLiteral, + type NumericLiteral, + type ReferenceLiteral, + type ValueKeywordLiteral, + isBinaryExpression, + isBlockTypeProperty, + isBooleanLiteral, + isCellRangeLiteral, + isCollectionLiteral, + isConstraintDefinition, + isExpressionConstraintDefinition, + isExpressionLiteral, + isFreeVariableLiteral, + isNumericLiteral, + isReferenceLiteral, + isRegexLiteral, + isTernaryExpression, + isTextLiteral, + isTransformDefinition, + isTransformPortDefinition, + isUnaryExpression, + isValueKeywordLiteral, + isValueLiteral, + isValuetypeAssignmentLiteral, +} from '../generated/ast'; +import { getNextAstNodeContainer } from '../model-util'; +import { + type ValueType, + type ValueTypeProvider, + type WrapperFactoryProvider, +} from '../wrappers'; +import { + getValuetypeHierarchyStack, + pickCommonAtomicValueType, + pickCommonPrimitiveValuetype, +} from '../wrappers/util/value-type-util'; + +import { isEveryValueDefined } from './typeguards'; + +export function inferExpressionType( + expression: Expression | undefined, + validationContext: ValidationContext, + valueTypeProvider: ValueTypeProvider, + wrapperFactories: WrapperFactoryProvider, +): ValueType | undefined { + if (expression === undefined) { + return undefined; + } + if (isExpressionLiteral(expression)) { + return inferTypeFromExpressionLiteral( + expression, + validationContext, + valueTypeProvider, + wrapperFactories, + ); + } + if (isUnaryExpression(expression)) { + const innerType = inferExpressionType( + expression.expression, + validationContext, + valueTypeProvider, + wrapperFactories, + ); + if (innerType === undefined) { + return undefined; + } + + const operator = expression.operator; + const typeComputer = validationContext.typeComputerRegistry.unary[operator]; + return typeComputer.computeType(innerType, expression, validationContext); + } + if (isBinaryExpression(expression)) { + const leftType = inferExpressionType( + expression.left, + validationContext, + valueTypeProvider, + wrapperFactories, + ); + const rightType = inferExpressionType( + expression.right, + validationContext, + valueTypeProvider, + wrapperFactories, + ); + if (leftType === undefined || rightType === undefined) { + return undefined; + } + + const operator = expression.operator; + const typeComputer = + validationContext.typeComputerRegistry.binary[operator]; + return typeComputer.computeType( + leftType, + rightType, + expression, + validationContext, + ); + } + if (isTernaryExpression(expression)) { + const firstType = inferExpressionType( + expression.first, + validationContext, + valueTypeProvider, + wrapperFactories, + ); + const secondType = inferExpressionType( + expression.second, + validationContext, + valueTypeProvider, + wrapperFactories, + ); + const thirdType = inferExpressionType( + expression.third, + validationContext, + valueTypeProvider, + wrapperFactories, + ); + if ( + firstType === undefined || + secondType === undefined || + thirdType === undefined + ) { + return undefined; + } + + const operator = expression.operator; + const typeComputer = + validationContext.typeComputerRegistry.ternary[operator]; + return typeComputer.computeType( + firstType, + secondType, + thirdType, + expression, + validationContext, + ); + } + assertUnreachable(expression); +} + +/** + * @returns the resolved value type or undefined (e.g. if a reference is not resolved) + */ +function inferTypeFromExpressionLiteral( + expression: ExpressionLiteral, + validationContext: ValidationContext, + valueTypeProvider: ValueTypeProvider, + wrapperFactories: WrapperFactoryProvider, +): ValueType | undefined { + if (isValueLiteral(expression)) { + if (isTextLiteral(expression)) { + return valueTypeProvider.Primitives.Text; + } else if (isBooleanLiteral(expression)) { + return valueTypeProvider.Primitives.Boolean; + } else if (isNumericLiteral(expression)) { + return inferNumericType(expression, valueTypeProvider); + } else if (isCellRangeLiteral(expression)) { + return valueTypeProvider.Primitives.CellRange; + } else if (isRegexLiteral(expression)) { + return valueTypeProvider.Primitives.Regex; + } else if (isValuetypeAssignmentLiteral(expression)) { + return valueTypeProvider.Primitives.ValuetypeAssignment; + } else if (isCollectionLiteral(expression)) { + return inferCollectionType( + expression, + validationContext, + valueTypeProvider, + wrapperFactories, + ); + } + assertUnreachable(expression); + } else if (isFreeVariableLiteral(expression)) { + if (isValueKeywordLiteral(expression)) { + return inferTypeFromValueKeyword( + expression, + validationContext, + valueTypeProvider, + wrapperFactories, + ); + } else if (isReferenceLiteral(expression)) { + return inferTypeFromReferenceLiteral( + expression, + valueTypeProvider, + wrapperFactories, + ); + } + assertUnreachable(expression); + } + assertUnreachable(expression); +} + +/** + * Infers the numeric type dependent on the value parsed to TypeScript. + * Thus, the inferred type might differ from the literal type. + * E.g., 3.0 is currently interpreted as integer but is a DecimalLiteral. + */ +function inferNumericType( + expression: NumericLiteral, + valueTypeProvider: ValueTypeProvider, +): ValueType { + if (Number.isInteger(expression.value)) { + return valueTypeProvider.Primitives.Integer; + } + return valueTypeProvider.Primitives.Decimal; +} + +function inferCollectionType( + collection: CollectionLiteral, + validationContext: ValidationContext, + valueTypeProvider: ValueTypeProvider, + wrapperFactories: WrapperFactoryProvider, +): ValueType | undefined { + const elementValuetypes = inferCollectionElementTypes( + collection, + validationContext, + valueTypeProvider, + wrapperFactories, + ); + if (elementValuetypes === undefined) { + return undefined; + } + + const stacks = elementValuetypes.map(getValuetypeHierarchyStack); + + if (stacks.length === 0) { + return valueTypeProvider.EmptyCollection; + } + if (stacks.length === 1) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const stack = stacks[0]!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const resultingInnerType = stack[stack.length - 1]!; + return valueTypeProvider.createCollectionValueTypeOf(resultingInnerType); + } + + const primitiveValuetypes = stacks.map((stack) => stack[0]); + + const commonPrimitiveValuetype = + pickCommonPrimitiveValuetype(primitiveValuetypes); + + if (commonPrimitiveValuetype === undefined) { + validationContext.accept( + 'error', + 'The type of the collection cannot be inferred from its elements', + { + node: collection, + }, + ); + return undefined; + } + + const commonAtomicValueType = pickCommonAtomicValueType(stacks); + if (commonAtomicValueType === undefined) { + return valueTypeProvider.createCollectionValueTypeOf( + commonPrimitiveValuetype, + ); + } + return valueTypeProvider.createCollectionValueTypeOf(commonAtomicValueType); +} + +function inferCollectionElementTypes( + collection: CollectionLiteral, + validationContext: ValidationContext, + valueTypeProvider: ValueTypeProvider, + wrapperFactories: WrapperFactoryProvider, +): ValueType[] | undefined { + const elementValuetypes = collection.values.map((value) => + inferExpressionType( + value, + validationContext, + valueTypeProvider, + wrapperFactories, + ), + ); + if (!isEveryValueDefined(elementValuetypes)) { + return undefined; + } + return elementValuetypes; +} + +function inferTypeFromValueKeyword( + expression: ValueKeywordLiteral, + validationContext: ValidationContext, + valueTypeProvider: ValueTypeProvider, + wrapperFactories: WrapperFactoryProvider, +): ValueType | undefined { + const expressionConstraintContainer = getNextAstNodeContainer( + expression, + isExpressionConstraintDefinition, + ); + if (expressionConstraintContainer === undefined) { + validationContext.accept( + 'error', + 'The value keyword is not allowed in this context', + { + node: expression, + }, + ); + return undefined; + } + + const valueType = wrapperFactories.ValueType.wrap( + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + expressionConstraintContainer?.valueType, + ); + if (valueType === undefined) { + return undefined; + } + + if (expression.lengthAccess) { + if (!valueType.isConvertibleTo(valueTypeProvider.Primitives.Text)) { + validationContext.accept( + 'error', + 'The length can only be accessed from text values ', + { + node: expression, + keyword: 'length', + }, + ); + return undefined; + } + return valueTypeProvider.Primitives.Integer; + } + return valueType; +} + +function inferTypeFromReferenceLiteral( + expression: ReferenceLiteral, + valueTypeProvider: ValueTypeProvider, + wrapperFactories: WrapperFactoryProvider, +): ValueType | undefined { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + const referenced = expression?.value?.ref; + if (referenced === undefined) { + return undefined; + } + + if (isConstraintDefinition(referenced)) { + return valueTypeProvider.Primitives.Constraint; + } + if (isTransformDefinition(referenced)) { + return valueTypeProvider.Primitives.Transform; + } + if ( + isTransformPortDefinition(referenced) || + isBlockTypeProperty(referenced) + ) { + const valueType = referenced.valueType; + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (valueType === undefined) { + return undefined; + } + return wrapperFactories.ValueType.wrap(valueType); + } + assertUnreachable(referenced); +} diff --git a/libs/language-server/src/lib/ast/expressions/typeguards.ts b/libs/language-server/src/lib/ast/expressions/typeguards.ts new file mode 100644 index 00000000..a630cda4 --- /dev/null +++ b/libs/language-server/src/lib/ast/expressions/typeguards.ts @@ -0,0 +1,126 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type pl } from 'nodejs-polars'; + +import { + type BlockTypeProperty, + type CellRangeLiteral, + type ConstraintDefinition, + type TransformDefinition, + type ValuetypeAssignment, + isBlockTypeProperty, + isCellRangeLiteral, + isConstraintDefinition, + isTransformDefinition, + isValuetypeAssignment, +} from '../generated/ast'; + +import { + type AtomicInternalValueRepresentation, + type InternalValueRepresentation, + type InternalValueRepresentationTypeguard, +} from './internal-value-representation'; + +export const NUMBER_TYPEGUARD: InternalValueRepresentationTypeguard<number> = ( + value: unknown, +): value is number => { + return typeof value === 'number'; +}; +export const BOOLEAN_TYPEGUARD: InternalValueRepresentationTypeguard< + boolean +> = (value: unknown): value is boolean => { + return typeof value === 'boolean'; +}; +export const STRING_TYPEGUARD: InternalValueRepresentationTypeguard<string> = ( + value: unknown, +): value is string => { + return typeof value === 'string'; +}; + +export const REGEXP_TYPEGUARD: InternalValueRepresentationTypeguard<RegExp> = ( + value: unknown, +): value is RegExp => { + return value instanceof RegExp; +}; + +export const CELL_RANGE_LITERAL_TYPEGUARD: InternalValueRepresentationTypeguard< + CellRangeLiteral +> = (value: unknown): value is CellRangeLiteral => { + return isCellRangeLiteral(value); +}; + +export const CONSTRAINT_DEFINITION_TYPEGUARD: InternalValueRepresentationTypeguard< + ConstraintDefinition +> = (value: unknown): value is ConstraintDefinition => { + return isConstraintDefinition(value); +}; + +export const VALUETYPE_ASSIGNMENT_TYPEGUARD: InternalValueRepresentationTypeguard< + ValuetypeAssignment +> = (value: unknown): value is ValuetypeAssignment => { + return isValuetypeAssignment(value); +}; + +export const BLOCKTYPE_PROPERTY_TYPEGUARD: InternalValueRepresentationTypeguard< + BlockTypeProperty +> = (value: unknown): value is BlockTypeProperty => { + return isBlockTypeProperty(value); +}; + +export const TRANSFORM_DEFINITION_TYPEGUARD: InternalValueRepresentationTypeguard< + TransformDefinition +> = (value: unknown): value is TransformDefinition => { + return isTransformDefinition(value); +}; + +export const ATOMIC_TYPE_REPRESENTAION_TYPEGUARD: InternalValueRepresentationTypeguard< + AtomicInternalValueRepresentation +> = (value: unknown): value is AtomicInternalValueRepresentation => { + return ( + BOOLEAN_TYPEGUARD(value) || + NUMBER_TYPEGUARD(value) || + STRING_TYPEGUARD(value) || + REGEXP_TYPEGUARD(value) || + CELL_RANGE_LITERAL_TYPEGUARD(value) || + CONSTRAINT_DEFINITION_TYPEGUARD(value) || + VALUETYPE_ASSIGNMENT_TYPEGUARD(value) || + BLOCKTYPE_PROPERTY_TYPEGUARD(value) || + TRANSFORM_DEFINITION_TYPEGUARD(value) + ); +}; + +export const INTERNAL_ARRAY_REPRESENTATION_TYPEGUARD: InternalValueRepresentationTypeguard< + InternalValueRepresentation[] +> = (value: unknown): value is InternalValueRepresentation[] => { + if (!Array.isArray(value)) { + return false; + } + return everyValueInternalRepresentationTypeguard( + value, + INTERNAL_VALUE_REPRESENTATION_TYPEGUARD, + ); +}; + +export const INTERNAL_VALUE_REPRESENTATION_TYPEGUARD: InternalValueRepresentationTypeguard< + InternalValueRepresentation +> = (value: unknown): value is InternalValueRepresentation => { + return ( + INTERNAL_ARRAY_REPRESENTATION_TYPEGUARD(value) || + ATOMIC_TYPE_REPRESENTAION_TYPEGUARD(value) + ); +}; + +export function everyValueInternalRepresentationTypeguard< + T extends InternalValueRepresentation, +>( + array: unknown[], + ELEMENT_TYPEGUARD: InternalValueRepresentationTypeguard<T>, +): array is T[] { + return array.every((value) => ELEMENT_TYPEGUARD(value)); +} + +export function isEveryValueDefined<T>(array: (T | undefined)[]): array is T[] { + return array.every((value) => value !== undefined); +} diff --git a/libs/language-server/src/lib/ast/index.ts b/libs/language-server/src/lib/ast/index.ts new file mode 100644 index 00000000..1d38bf0a --- /dev/null +++ b/libs/language-server/src/lib/ast/index.ts @@ -0,0 +1,15 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +export * from './expressions'; + +export * from './generated/ast'; +export * from './generated/grammar'; +export * from './generated/module'; + +export * from './wrappers'; + +export * from './model-util'; + +export * from './io-type'; diff --git a/libs/language-server/src/lib/ast/io-type.ts b/libs/language-server/src/lib/ast/io-type.ts new file mode 100644 index 00000000..1ae88034 --- /dev/null +++ b/libs/language-server/src/lib/ast/io-type.ts @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; + +import { type BlockTypeInput, type BlockTypeOutput } from './generated/ast'; + +export enum IOType { + NONE = 'None', + FILE = 'File', + TEXT_FILE = 'TextFile', + FILE_SYSTEM = 'FileSystem', + SHEET = 'Sheet', + TABLE = 'Table', + WORKBOOK = 'Workbook', +} + +export function getIOType(blockIO: BlockTypeInput | BlockTypeOutput): IOType { + const ioTypeName = blockIO.iotype.ref?.name; + assert( + ioTypeName !== undefined, + `Unknown IOType name for block input/output ${blockIO.name}.`, + ); + + assert( + // eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison + Object.values(IOType).some((type) => type === ioTypeName), + `IO type ${ioTypeName} does not exist.`, + ); + return ioTypeName as IOType; +} diff --git a/libs/language-server/src/lib/ast/model-util.ts b/libs/language-server/src/lib/ast/model-util.ts new file mode 100644 index 00000000..dae9011b --- /dev/null +++ b/libs/language-server/src/lib/ast/model-util.ts @@ -0,0 +1,120 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type AstNode, type LangiumDocuments } from 'langium'; + +import { + type BuiltinBlockTypeDefinition, + type BuiltinConstrainttypeDefinition, + isBuiltinBlockTypeDefinition, + isJayveeModel, +} from './generated/ast'; +import { + type BlockTypeWrapper, + type ConstraintTypeWrapper, + type WrapperFactoryProvider, +} from './wrappers'; + +export type AstTypeGuard<T extends AstNode = AstNode> = ( + obj: unknown, +) => obj is T; + +/** + * Recursively goes upwards through the AST until it finds an AST node that satisfies the given type guard. + * The entered AST node itself cannot be the result. + * @param node The current AST node to start the search from. + * @param guard The type guard function to check if a container matches the desired type. + * @returns The desired container node that satisfies the type guard, or undefined if not found. + */ +export function getNextAstNodeContainer<T extends AstNode>( + node: AstNode, + guard: AstTypeGuard<T>, +): T | undefined { + if (node.$container === undefined) { + return undefined; + } + if (guard(node.$container)) { + return node.$container; + } + return getNextAstNodeContainer(node.$container, guard); +} + +/** + * Utility function that gets all builtin block types. + * Duplicates are only added once. + * Make sure to call @see initializeWorkspace first so that the file system is initialized. + */ +export function getAllBuiltinBlockTypes( + documentService: LangiumDocuments, + wrapperFactories: WrapperFactoryProvider, +): BlockTypeWrapper[] { + const allBuiltinBlockTypes: BlockTypeWrapper[] = []; + const visitedBuiltinBlockTypeDefinitions = + new Set<BuiltinBlockTypeDefinition>(); + + documentService.all + .map((document) => document.parseResult.value) + .forEach((parsedDocument) => { + if (!isJayveeModel(parsedDocument)) { + throw new Error('Expected parsed document to be a JayveeModel'); + } + parsedDocument.blockTypes.forEach((blockTypeDefinition) => { + if (!isBuiltinBlockTypeDefinition(blockTypeDefinition)) { + return; + } + + const wasAlreadyVisited = + visitedBuiltinBlockTypeDefinitions.has(blockTypeDefinition); + if (wasAlreadyVisited) { + return; + } + + if (wrapperFactories.BlockType.canWrap(blockTypeDefinition)) { + allBuiltinBlockTypes.push( + wrapperFactories.BlockType.wrap(blockTypeDefinition), + ); + visitedBuiltinBlockTypeDefinitions.add(blockTypeDefinition); + } + }); + }); + return allBuiltinBlockTypes; +} + +/** + * Utility function that gets all builtin constraint types. + * Duplicates are only added once. + * Make sure to call @see initializeWorkspace first so that the file system is initialized. + */ +export function getAllBuiltinConstraintTypes( + documentService: LangiumDocuments, + wrapperFactories: WrapperFactoryProvider, +): ConstraintTypeWrapper[] { + const allBuiltinConstraintTypes: ConstraintTypeWrapper[] = []; + const visitedBuiltinConstraintTypeDefinitions = + new Set<BuiltinConstrainttypeDefinition>(); + + documentService.all + .map((document) => document.parseResult.value) + .forEach((parsedDocument) => { + if (!isJayveeModel(parsedDocument)) { + throw new Error('Expected parsed document to be a JayveeModel'); + } + parsedDocument.constrainttypes.forEach((constraintTypeDefinition) => { + const wasAlreadyVisited = visitedBuiltinConstraintTypeDefinitions.has( + constraintTypeDefinition, + ); + if (wasAlreadyVisited) { + return; + } + + if (wrapperFactories.ConstraintType.canWrap(constraintTypeDefinition)) { + allBuiltinConstraintTypes.push( + wrapperFactories.ConstraintType.wrap(constraintTypeDefinition), + ); + visitedBuiltinConstraintTypeDefinitions.add(constraintTypeDefinition); + } + }); + }); + return allBuiltinConstraintTypes; +} diff --git a/libs/language-server/src/lib/ast/value-definition.spec.ts b/libs/language-server/src/lib/ast/value-definition.spec.ts new file mode 100644 index 00000000..11df61fd --- /dev/null +++ b/libs/language-server/src/lib/ast/value-definition.spec.ts @@ -0,0 +1,72 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type AstNode, type LangiumDocument } from 'langium'; +import { NodeFileSystem } from 'langium/node'; + +import { type ParseHelperOptions, parseHelper } from '../../test/langium-utils'; +import { readJvTestAssetHelper } from '../../test/utils'; +import { createJayveeServices } from '../jayvee-module'; + +describe('Parsing of ValuetypeDefinition', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../test/assets/', + ); + + beforeAll(() => { + const services = createJayveeServices(NodeFileSystem).Jayvee; + parse = parseHelper(services); + }); + + it('should diagnose error on missing builtin keyword', async () => { + const text = readJvTestAsset( + 'value-type-definition/invalid-missing-builtin-keyword.jv', + ); + + const document = await parse(text); + expect(document.parseResult.parserErrors.length).toBeGreaterThanOrEqual(1); + expect(document.parseResult.parserErrors[0]?.message).toBe( + "Expecting token of type 'oftype' but found `;`.", + ); + }); + + it('should diagnose error on unallowed body for builtin value types', async () => { + const text = readJvTestAsset( + 'value-type-definition/invalid-unallowed-builtin-body.jv', + ); + + const document = await parse(text); + expect(document.parseResult.parserErrors.length).toBeGreaterThanOrEqual(1); + expect(document.parseResult.parserErrors[0]?.message).toBe( + "Expecting token of type ';' but found `{`.", + ); + }); + + it('should diagnose error on empty generic', async () => { + const text = readJvTestAsset( + 'value-type-definition/invalid-missing-generic.jv', + ); + + const document = await parse(text); + expect(document.parseResult.parserErrors.length).toBeGreaterThanOrEqual(1); + expect(document.parseResult.parserErrors[0]?.message).toBe( + "Expecting token of type 'ID' but found `>`.", + ); + }); + + it('should diagnose no error on generic', async () => { + const text = readJvTestAsset( + 'value-type-definition/valid-builtin-value-type-generic.jv', + ); + + const document = await parse(text); + expect(document.parseResult.parserErrors.length).toBe(0); + }); +}); diff --git a/libs/language-server/src/lib/ast/wrappers/ast-node-wrapper.ts b/libs/language-server/src/lib/ast/wrappers/ast-node-wrapper.ts new file mode 100644 index 00000000..d8deb34c --- /dev/null +++ b/libs/language-server/src/lib/ast/wrappers/ast-node-wrapper.ts @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type AstNode } from 'langium'; + +export interface AstNodeWrapper<N extends AstNode> { + readonly astNode: N; +} diff --git a/libs/language-server/src/lib/ast/wrappers/cell-range-wrapper.ts b/libs/language-server/src/lib/ast/wrappers/cell-range-wrapper.ts new file mode 100644 index 00000000..9a8f8e87 --- /dev/null +++ b/libs/language-server/src/lib/ast/wrappers/cell-range-wrapper.ts @@ -0,0 +1,213 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; + +import { assertUnreachable } from 'langium'; + +import { + type CellLiteral, + type CellRangeLiteral, + type CellReference, + type ColumnId, + type ColumnLiteral, + type RangeLiteral, + type RowId, + type RowLiteral, + isCellLiteral, + isCellReference, + isColumnLiteral, + isRangeLiteral, + isRowLiteral, +} from '../generated/ast'; + +import { type AstNodeWrapper } from './ast-node-wrapper'; +import { CellIndex, type CellIndexBounds, LAST_INDEX } from './util/cell-index'; +import { columnCharactersAsIndex } from './util/column-id-util'; + +export class CellRangeWrapper<N extends CellRangeLiteral = CellRangeLiteral> + implements AstNodeWrapper<N> +{ + public readonly astNode: N; + public readonly from: CellIndex; + public readonly to: CellIndex; + + constructor(cellRange: N, indexes?: { from: CellIndex; to: CellIndex }) { + this.astNode = cellRange; + + if (indexes !== undefined) { + this.from = indexes.from; + this.to = indexes.to; + } else if (isCellLiteral(cellRange)) { + const cellIndex = parseCellReference(cellRange.cellId); + this.from = cellIndex; + this.to = cellIndex; + } else if (isColumnLiteral(cellRange)) { + const columnIndex = parseColumnId(cellRange.columnId.value); + this.from = new CellIndex(columnIndex, 0); + this.to = new CellIndex(columnIndex, LAST_INDEX); + } else if (isRowLiteral(cellRange)) { + const rowIndex = parseRowId(cellRange.rowId.value); + this.from = new CellIndex(0, rowIndex); + this.to = new CellIndex(LAST_INDEX, rowIndex); + } else { + this.from = parseCellReference(cellRange.cellFrom); + this.to = parseCellReference(cellRange.cellTo); + } + } + + static canBeWrapped(cellRange: CellRangeLiteral): boolean { + if (isCellLiteral(cellRange)) { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + return isCompleteCellReference(cellRange?.cellId); + } else if (isColumnLiteral(cellRange)) { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + return isCompleteColumnId(cellRange?.columnId); + } else if (isRowLiteral(cellRange)) { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + return isCompleteRowId(cellRange?.rowId); + } else if (isRangeLiteral(cellRange)) { + return ( + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + isCompleteCellReference(cellRange?.cellFrom) && + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + isCompleteCellReference(cellRange?.cellTo) + ); + } + assertUnreachable(cellRange); + } + + isOneDimensional(): boolean { + return ( + this.from.columnIndex === this.to.columnIndex || + this.from.rowIndex === this.to.rowIndex + ); + } + + numberOfCells(): number { + const numberOfRows = this.getNumberOfRows(); + const numberOfColumns = this.getNumberOfColumns(); + + return numberOfRows * numberOfColumns; + } + + private getNumberOfRows(): number { + if (this.from.rowIndex === LAST_INDEX && this.to.rowIndex === LAST_INDEX) { + return 1; + } + return this.to.rowIndex - this.from.rowIndex + 1; + } + + private getNumberOfColumns(): number { + if ( + this.from.columnIndex === LAST_INDEX && + this.to.columnIndex === LAST_INDEX + ) { + return 1; + } + return this.to.columnIndex - this.from.columnIndex + 1; + } + + isInBounds(bounds: CellIndexBounds): boolean { + return this.from.isInBounds(bounds) && this.to.isInBounds(bounds); + } + + resolveRelativeIndexes(bounds: CellIndexBounds): CellRangeWrapper<N> { + const boundFrom = this.from.resolveRelativeIndexes(bounds); + const boundTo = this.to.resolveRelativeIndexes(bounds); + return new CellRangeWrapper<N>(this.astNode, { + from: boundFrom, + to: boundTo, + }); + } + + hasRelativeIndexes(): boolean { + return this.from.hasRelativeIndexes() || this.to.hasRelativeIndexes(); + } + + toString(): string { + return `${this.from.toString()}:${this.to.toString()}`; + } +} + +export type ColumnWrapper = CellRangeWrapper<ColumnLiteral | RangeLiteral>; +export type RowWrapper = CellRangeWrapper<RowLiteral | RangeLiteral>; +export type CellWrapper = CellRangeWrapper<CellLiteral | RangeLiteral>; + +function isCompleteCellReference( + cellReference: string | CellReference | undefined, +): boolean { + if (isCellReference(cellReference)) { + return ( + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + isCompleteColumnId(cellReference?.columnId) && + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + isCompleteRowId(cellReference?.rowId) + ); + } + return cellReference !== undefined; +} + +function isCompleteColumnId(columnId: ColumnId | undefined) { + return columnId?.value !== undefined; +} + +function isCompleteRowId(rowId: RowId | undefined) { + return rowId?.value !== undefined; +} + +function parseCellReference(cellReference: string | CellReference): CellIndex { + // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents + let columnId: string | '*'; + let rowId: number | '*'; + if (typeof cellReference === 'string') { + const parseResult = parseStringCellReference(cellReference); + columnId = parseResult.columnId; + rowId = parseResult.rowId; + } else { + columnId = cellReference.columnId.value; + rowId = cellReference.rowId.value; + } + + const columnIndex = parseColumnId(columnId); + const rowIndex = parseRowId(rowId); + return new CellIndex(columnIndex, rowIndex); +} + +const CELL_REFERENCE_REGEX = /([A-Z]+)([0-9]+)/; + +function parseStringCellReference(cellReference: string): { + columnId: string; + rowId: number; +} { + const matches = CELL_REFERENCE_REGEX.exec(cellReference) || []; + const columnIdMatch = matches[1]; + const rowIdMatch = matches[2]; + + assert( + columnIdMatch !== undefined && rowIdMatch !== undefined, + `Cell IDs are expected to match the regular expression ${CELL_REFERENCE_REGEX.toString()}`, + ); + + return { + columnId: columnIdMatch, + rowId: Number.parseInt(rowIdMatch, 10), + }; +} + +// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents +function parseColumnId(columnId: string | '*'): number { + if (columnId === '*') { + return LAST_INDEX; + } + return columnCharactersAsIndex(columnId); +} + +function parseRowId(rowId: number | '*'): number { + if (rowId === '*') { + return LAST_INDEX; + } + return rowId - 1; +} diff --git a/libs/language-server/src/lib/ast/wrappers/index.ts b/libs/language-server/src/lib/ast/wrappers/index.ts new file mode 100644 index 00000000..3dc2d8df --- /dev/null +++ b/libs/language-server/src/lib/ast/wrappers/index.ts @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +export * from './wrapper-factory-provider'; +export * from './util'; + +/* + * Note: Only export types if possible to enforce usage of WrapperFactory outside this directory. + * This allows us to avoid dependency cycles between the language server and interpreter. + */ + +export { type AstNodeWrapper } from './ast-node-wrapper'; +export { + type CellRangeWrapper, + type CellWrapper, + type ColumnWrapper, + type RowWrapper, +} from './cell-range-wrapper'; +export { type PipeWrapper } from './pipe-wrapper'; +export { type PipelineWrapper } from './pipeline-wrapper'; + +export { type BlockTypeWrapper } from './typed-object/block-type-wrapper'; +export { type CompositeBlockTypeWrapper } from './typed-object/composite-block-type-wrapper'; +export { type ConstraintTypeWrapper } from './typed-object/constrainttype-wrapper'; +export { + ExampleDoc, + PropertyDocs, + PropertySpecification, + type TypedObjectWrapper, +} from './typed-object/typed-object-wrapper'; + +export * from './value-type'; // type export handled one level deeper diff --git a/libs/language-server/src/lib/ast/wrappers/pipe-wrapper.ts b/libs/language-server/src/lib/ast/wrappers/pipe-wrapper.ts new file mode 100644 index 00000000..fb627290 --- /dev/null +++ b/libs/language-server/src/lib/ast/wrappers/pipe-wrapper.ts @@ -0,0 +1,72 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; + +import { type DiagnosticInfo } from 'langium'; + +import { + type BlockDefinition, + type BlockTypePipeline, + type PipeDefinition, +} from '../generated/ast'; + +import { type AstNodeWrapper } from './ast-node-wrapper'; + +export class PipeWrapper< + T extends PipeDefinition | BlockTypePipeline = + | PipeDefinition + | BlockTypePipeline, +> implements AstNodeWrapper<T> +{ + public readonly astNode: T; + private readonly chainIndex: number; + public readonly from: BlockDefinition; + public readonly to: BlockDefinition; + + constructor(pipe: T, chainIndex: number) { + this.astNode = pipe; + assert(0 <= chainIndex && chainIndex + 1 < pipe.blocks.length); + assert(pipe.blocks[chainIndex]?.ref !== undefined); + assert(pipe.blocks[chainIndex + 1]?.ref !== undefined); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this.from = pipe.blocks[chainIndex]!.ref!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this.to = pipe.blocks[chainIndex + 1]!.ref!; + this.chainIndex = chainIndex; + } + + getFromDiagnostic(): DiagnosticInfo<PipeDefinition | BlockTypePipeline> { + return { + node: this.astNode, + property: 'blocks', + index: this.chainIndex, + }; + } + + getToDiagnostic(): DiagnosticInfo<PipeDefinition | BlockTypePipeline> { + return { + node: this.astNode, + property: 'blocks', + index: this.chainIndex + 1, + }; + } + + equals(pipe: PipeWrapper<T>): boolean { + return this.from === pipe.from && this.to === pipe.to; + } + + static canBeWrapped( + pipe: PipeDefinition | BlockTypePipeline, + chainIndex: number, + ): boolean { + return ( + 0 <= chainIndex && + chainIndex + 1 < pipe.blocks.length && + pipe.blocks[chainIndex]?.ref !== undefined && + pipe.blocks[chainIndex + 1]?.ref !== undefined + ); + } +} diff --git a/libs/language-server/src/lib/ast/wrappers/pipeline-wrapper.ts b/libs/language-server/src/lib/ast/wrappers/pipeline-wrapper.ts new file mode 100644 index 00000000..03e9f3de --- /dev/null +++ b/libs/language-server/src/lib/ast/wrappers/pipeline-wrapper.ts @@ -0,0 +1,154 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; + +import { + type BlockDefinition, + type CompositeBlockTypeDefinition, + type PipelineDefinition, +} from '../generated/ast'; + +import { type AstNodeWrapper } from './ast-node-wrapper'; +import { type PipeWrapper } from './pipe-wrapper'; +import { type IPipeWrapperFactory } from './wrapper-factory-provider'; + +export class PipelineWrapper< + T extends PipelineDefinition | CompositeBlockTypeDefinition, +> implements AstNodeWrapper<T> +{ + public readonly astNode: T; + + allPipes: PipeWrapper[] = []; + + constructor(pipesContainer: T, pipeWrapperFactory: IPipeWrapperFactory) { + this.astNode = pipesContainer; + + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (pipesContainer.pipes === undefined) { + this.allPipes = []; + return; + } + + this.allPipes = pipesContainer.pipes.flatMap((pipe) => + pipeWrapperFactory.wrapAll(pipe), + ); + } + + static canBeWrapped( + pipesContainer: PipelineDefinition | CompositeBlockTypeDefinition, + pipeWrapperFactory: IPipeWrapperFactory, + ): boolean { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (pipesContainer.pipes === undefined) { + return true; + } + + for (const pipeDefinition of pipesContainer.pipes) { + for ( + let chainIndex = 0; + chainIndex < pipeDefinition.blocks.length - 1; + ++chainIndex + ) { + if (!pipeWrapperFactory.canWrap(pipeDefinition, chainIndex)) { + return false; + } + } + } + return true; + } + + getStartingBlockPipes(): PipeWrapper[] { + return this.allPipes.filter((pipe) => { + const parentBlock = pipe.from; + const isToOfOtherPipe = + this.allPipes.filter((p) => p.to === parentBlock).length > 0; + return !isToOfOtherPipe; + }); + } + + getStartingBlocks(): BlockDefinition[] { + const startingBlocks = this.getStartingBlockPipes().map((p) => p.from); + + // Special case: the extractor is reused for multiple paths + // Thus, we remove duplicates + const withoutDuplicates = [...new Set(startingBlocks)]; + return withoutDuplicates; + } + + getOutgoingPipes(blockDefinition: BlockDefinition): PipeWrapper[] { + return this.allPipes.filter((pipe) => { + return pipe.from === blockDefinition; + }); + } + + getChildBlocks(blockDefinition: BlockDefinition): BlockDefinition[] { + return this.getOutgoingPipes(blockDefinition).map((p) => p.to); + } + + getIngoingPipes(blockDefinition: BlockDefinition): PipeWrapper[] { + return this.allPipes.filter((pipe) => { + return pipe.to === blockDefinition; + }); + } + + getParentBlocks(blockDefinition: BlockDefinition): BlockDefinition[] { + return this.getIngoingPipes(blockDefinition).map((p) => p.from); + } + + /** + * Returns blocks in a pipeline in topological order, based on + * Kahn's algorithm. + * + * Considers a pipeline as a directed, acyclical graph where + * blocks are nodes and pipes are edges. A list in topological + * order has the property that parent nodes are always listed + * before their children. + * + * "[...] a list in topological order is such that no element + * appears in it until after all elements appearing on all paths + * leading to the particular element have been listed." + * + * Kahn, A. B. (1962). Topological sorting of large networks. Communications of the ACM, 5(11), 558–562. + */ + getBlocksInTopologicalSorting(): BlockDefinition[] { + const sortedNodes = []; + const currentNodes = [...this.getStartingBlocks()]; + let unvisitedEdges = [...this.allPipes]; + + while (currentNodes.length > 0) { + const node = currentNodes.pop(); + assert(node !== undefined); + + sortedNodes.push(node); + + for (const childNode of this.getChildBlocks(node)) { + // Mark edges between parent and child as visited + this.getIngoingPipes(childNode) + .filter((e) => e.from === node) + .forEach((e) => { + unvisitedEdges = unvisitedEdges.filter((edge) => !edge.equals(e)); + }); + + // If all edges to the child have been visited + const notRemovedEdges = this.getIngoingPipes(childNode).filter((e) => + unvisitedEdges.some((edge) => edge.equals(e)), + ); + if (notRemovedEdges.length === 0) { + // Insert it into currentBlocks + currentNodes.push(childNode); + } + } + } + + // If the graph still contains unvisited edges it is not a DAG + assert( + unvisitedEdges.length === 0, + `The pipeline ${this.astNode.name} is expected to have no cycles`, + ); + + return sortedNodes; + } +} diff --git a/libs/language-server/src/lib/ast/wrappers/typed-object/block-type-wrapper.ts b/libs/language-server/src/lib/ast/wrappers/typed-object/block-type-wrapper.ts new file mode 100644 index 00000000..f61dd2c7 --- /dev/null +++ b/libs/language-server/src/lib/ast/wrappers/typed-object/block-type-wrapper.ts @@ -0,0 +1,140 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; + +import { type Reference, isReference } from 'langium'; + +import { RuntimeParameterProvider } from '../../../services'; +import { + EvaluationContext, + type OperatorEvaluatorRegistry, + evaluateExpression, +} from '../../expressions'; +import { type ReferenceableBlockTypeDefinition } from '../../generated/ast'; +import { IOType, getIOType } from '../../io-type'; +import { type ValueTypeProvider } from '../value-type'; +import { type WrapperFactoryProvider } from '../wrapper-factory-provider'; + +import { + type ExampleDoc, + type PropertySpecification, + TypedObjectWrapper, +} from './typed-object-wrapper'; + +interface BlockDocs { + description?: string; + examples?: ExampleDoc[]; +} + +export class BlockTypeWrapper extends TypedObjectWrapper<ReferenceableBlockTypeDefinition> { + docs: BlockDocs = {}; + + readonly inputType: IOType; + readonly outputType: IOType; + + /** + * Creates a BlockTypeWrapper if possible. Otherwise, throws error. + * Use @see canBeWrapped to check whether wrapping will be successful. + * + * Use @see WrapperFactoryProvider for instantiation instead of calling this constructor directly. + */ + constructor( + toBeWrapped: + | ReferenceableBlockTypeDefinition + | Reference<ReferenceableBlockTypeDefinition>, + operatorEvaluatorRegistry: OperatorEvaluatorRegistry, + valueTypeProvider: ValueTypeProvider, + wrapperFactories: WrapperFactoryProvider, + ) { + const blockTypeDefinition = isReference(toBeWrapped) + ? toBeWrapped.ref + : toBeWrapped; + assert(blockTypeDefinition !== undefined); + + const blockTypeName = blockTypeDefinition.name; + + const properties: Record<string, PropertySpecification> = {}; + for (const property of blockTypeDefinition.properties) { + const valueType = wrapperFactories.ValueType.wrap(property.valueType); + assert(valueType !== undefined); + + properties[property.name] = { + type: valueType, + }; + + const defaultValue = evaluateExpression( + property.defaultValue, + new EvaluationContext( + new RuntimeParameterProvider(), + operatorEvaluatorRegistry, + valueTypeProvider, + ), + wrapperFactories, + ); + if (defaultValue !== undefined) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + properties[property.name]!.defaultValue = defaultValue; + } + } + + super(blockTypeDefinition, blockTypeName, properties, undefined); + + const inputPort = blockTypeDefinition.inputs[0]; + assert(inputPort !== undefined); + this.inputType = getIOType(inputPort); + + const outputPort = blockTypeDefinition.outputs[0]; + assert(outputPort !== undefined); + this.outputType = getIOType(outputPort); + } + + static canBeWrapped( + toBeWrapped: + | ReferenceableBlockTypeDefinition + | Reference<ReferenceableBlockTypeDefinition>, + ): boolean { + const blockTypeDefinition = isReference(toBeWrapped) + ? toBeWrapped.ref + : toBeWrapped; + + if (blockTypeDefinition === undefined) { + return false; + } + + if ( + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + blockTypeDefinition.properties === undefined || + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + blockTypeDefinition.name === undefined || + blockTypeDefinition.inputs[0] === undefined || + blockTypeDefinition.outputs[0] === undefined + ) { + return false; + } + + if ( + blockTypeDefinition.properties.some((property) => { + return property.valueType.reference.ref === undefined; + }) + ) { + return false; + } + + return true; + } + + canBeConnectedTo(blockAfter: BlockTypeWrapper): boolean { + return this.outputType === blockAfter.inputType; + } + + hasInput(): boolean { + return this.inputType !== IOType.NONE; + } + + hasOutput(): boolean { + return this.outputType !== IOType.NONE; + } +} diff --git a/libs/language-server/src/lib/ast/wrappers/typed-object/composite-block-type-wrapper.ts b/libs/language-server/src/lib/ast/wrappers/typed-object/composite-block-type-wrapper.ts new file mode 100644 index 00000000..3667f39c --- /dev/null +++ b/libs/language-server/src/lib/ast/wrappers/typed-object/composite-block-type-wrapper.ts @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type OperatorEvaluatorRegistry } from '../../expressions/operator-registry'; +import { type CompositeBlockTypeDefinition } from '../../generated/ast'; +import { type ValueTypeProvider } from '../value-type'; +import { type WrapperFactoryProvider } from '../wrapper-factory-provider'; + +import { BlockTypeWrapper } from './block-type-wrapper'; + +export class CompositeBlockTypeWrapper extends BlockTypeWrapper { + /** + * Creates a CompositeBlockTypeWrapper if possible. Otherwise, throws error. + * Use @see canBeWrapped to check whether wrapping will be successful. + * + * Use @see WrapperFactoryProvider for instantiation instead of calling this constructor directly. + */ + constructor( + private blockTypeDefinition: CompositeBlockTypeDefinition, + operatorEvaluatorRegistry: OperatorEvaluatorRegistry, + valueTypeProvider: ValueTypeProvider, + wrapperFactories: WrapperFactoryProvider, + ) { + super( + blockTypeDefinition, + operatorEvaluatorRegistry, + valueTypeProvider, + wrapperFactories, + ); + } + + override getMissingRequiredPropertyNames( + presentPropertyNames: string[] = [], + ): string[] { + const missingRequiredPropertyNames = super.getMissingRequiredPropertyNames( + presentPropertyNames, + ); + + // We assume block type properties that have an expression as default value can be evaluated during runtime + return missingRequiredPropertyNames.filter((propertyName) => { + const blockTypeProperty = this.blockTypeDefinition.properties.find( + (blockTypeProperty) => blockTypeProperty.name === propertyName, + ); + + return blockTypeProperty?.defaultValue === undefined; + }); + } +} diff --git a/libs/language-server/src/lib/ast/wrappers/typed-object/constrainttype-wrapper.ts b/libs/language-server/src/lib/ast/wrappers/typed-object/constrainttype-wrapper.ts new file mode 100644 index 00000000..1889c53e --- /dev/null +++ b/libs/language-server/src/lib/ast/wrappers/typed-object/constrainttype-wrapper.ts @@ -0,0 +1,121 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; + +import { type Reference, isReference } from 'langium'; + +import { RuntimeParameterProvider } from '../../../services'; +import { evaluateExpression } from '../../expressions/evaluate-expression'; +import { EvaluationContext } from '../../expressions/evaluation-context'; +import { type OperatorEvaluatorRegistry } from '../../expressions/operator-registry'; +import { type BuiltinConstrainttypeDefinition } from '../../generated/ast'; +import { type ValueType, type ValueTypeProvider } from '../value-type'; +import { type WrapperFactoryProvider } from '../wrapper-factory-provider'; + +import { + type ExampleDoc, + type PropertySpecification, + TypedObjectWrapper, +} from './typed-object-wrapper'; + +interface ConstraintDocs { + description?: string; + examples?: ExampleDoc[]; +} + +export class ConstraintTypeWrapper extends TypedObjectWrapper<BuiltinConstrainttypeDefinition> { + docs: ConstraintDocs = {}; + readonly on: ValueType; + + /** + * Creates a ConstraintTypeWrapper if possible. Otherwise, throws error. + * Use @see canBeWrapped to check whether wrapping will be successful. + * + * Use @see WrapperFactoryProvider for instantiation instead of calling this constructor directly. + */ + constructor( + toBeWrapped: + | BuiltinConstrainttypeDefinition + | Reference<BuiltinConstrainttypeDefinition>, + operatorEvaluatorRegistry: OperatorEvaluatorRegistry, + valueTypeProvider: ValueTypeProvider, + wrapperFactories: WrapperFactoryProvider, + ) { + const constraintTypeDefinition = isReference(toBeWrapped) + ? toBeWrapped.ref + : toBeWrapped; + assert(constraintTypeDefinition !== undefined); + + const constraintTypeName = constraintTypeDefinition.name; + + const properties: Record<string, PropertySpecification> = {}; + for (const property of constraintTypeDefinition.properties) { + const valueType = wrapperFactories.ValueType.wrap(property.valueType); + assert(valueType !== undefined); + + properties[property.name] = { + type: valueType, + }; + + const defaultValue = evaluateExpression( + property.defaultValue, + new EvaluationContext( + new RuntimeParameterProvider(), + operatorEvaluatorRegistry, + valueTypeProvider, + ), + wrapperFactories, + ); + if (defaultValue !== undefined) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + properties[property.name]!.defaultValue = defaultValue; + } + } + + super(constraintTypeDefinition, constraintTypeName, properties, undefined); + + const valueType = wrapperFactories.ValueType.wrap( + constraintTypeDefinition.valueType, + ); + assert(valueType !== undefined); + this.on = valueType; + } + + static canBeWrapped( + toBeWrapped: + | BuiltinConstrainttypeDefinition + | Reference<BuiltinConstrainttypeDefinition>, + ): boolean { + const constraintTypeDefinition = isReference(toBeWrapped) + ? toBeWrapped.ref + : toBeWrapped; + + if (constraintTypeDefinition === undefined) { + return false; + } + + if ( + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + constraintTypeDefinition.properties === undefined || + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + constraintTypeDefinition.name === undefined || + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + constraintTypeDefinition.valueType === undefined + ) { + return false; + } + + if ( + constraintTypeDefinition.properties.some((property) => { + return property.valueType.reference.ref === undefined; + }) + ) { + return false; + } + + return true; + } +} diff --git a/libs/language-server/src/lib/ast/wrappers/typed-object/typed-object-wrapper.ts b/libs/language-server/src/lib/ast/wrappers/typed-object/typed-object-wrapper.ts new file mode 100644 index 00000000..02670b10 --- /dev/null +++ b/libs/language-server/src/lib/ast/wrappers/typed-object/typed-object-wrapper.ts @@ -0,0 +1,120 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type AstNode } from 'langium'; + +import { type ValidationContext } from '../../../validation/validation-context'; +import { type EvaluationContext } from '../../expressions/evaluation-context'; +import { type InternalValueRepresentation } from '../../expressions/internal-value-representation'; +import { + type PropertyAssignment, + type PropertyBody, +} from '../../generated/ast'; +import { type AstNodeWrapper } from '../ast-node-wrapper'; +import { type ValueType } from '../value-type'; + +export interface PropertySpecification< + I extends InternalValueRepresentation = InternalValueRepresentation, +> { + type: ValueType<I>; + defaultValue?: I; + validation?: ( + property: PropertyAssignment, + validationContext: ValidationContext, + evaluationContext: EvaluationContext, + ) => void; + docs?: PropertyDocs; +} + +export interface ExampleDoc { + code: string; + description: string; +} + +export interface PropertyDocs { + description?: string; + examples?: ExampleDoc[]; + validation?: string; +} + +export abstract class TypedObjectWrapper<N extends AstNode = AstNode> + implements AstNodeWrapper<N> +{ + protected constructor( + public readonly astNode: N, + public readonly type: string, + private readonly properties: Record<string, PropertySpecification>, + private readonly validation?: ( + property: PropertyBody, + validationContext: ValidationContext, + evaluationContext: EvaluationContext, + ) => void, + ) {} + + validate( + propertyBody: PropertyBody, + validationContext: ValidationContext, + evaluationContext: EvaluationContext, + ): void { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + const properties = propertyBody?.properties ?? []; + for (const property of properties) { + const propertySpecification = this.getPropertySpecification( + property.name, + ); + const propertyValidationFn = propertySpecification?.validation; + if (propertyValidationFn === undefined) { + continue; + } + propertyValidationFn(property, validationContext, evaluationContext); + } + + if (this.validation !== undefined) { + this.validation(propertyBody, validationContext, evaluationContext); + } + } + + getPropertySpecification( + name: string | undefined, + ): PropertySpecification | undefined { + if (name === undefined) { + return undefined; + } + return this.properties[name]; + } + + getPropertySpecifications(): Record<string, PropertySpecification> { + return this.properties; + } + + hasPropertySpecification(name: string): boolean { + return this.getPropertySpecification(name) !== undefined; + } + + getMissingRequiredPropertyNames( + presentPropertyNames: string[] = [], + ): string[] { + return this.getPropertyNames('required', presentPropertyNames); + } + + getPropertyNames( + kind: 'optional' | 'required' | undefined = undefined, + excludeNames: string[] = [], + ): string[] { + const resultingPropertyNames: string[] = []; + for (const [name, spec] of Object.entries(this.properties)) { + if (kind === 'optional' && spec.defaultValue === undefined) { + continue; + } + if (kind === 'required' && spec.defaultValue !== undefined) { + continue; + } + if (excludeNames.includes(name)) { + continue; + } + resultingPropertyNames.push(name); + } + return resultingPropertyNames; + } +} diff --git a/libs/language-server/src/lib/ast/wrappers/util/cell-index.ts b/libs/language-server/src/lib/ast/wrappers/util/cell-index.ts new file mode 100644 index 00000000..fe9af8fd --- /dev/null +++ b/libs/language-server/src/lib/ast/wrappers/util/cell-index.ts @@ -0,0 +1,75 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { columnIndexAsCharacters } from './column-id-util'; + +export const LAST_INDEX = Number.MAX_SAFE_INTEGER; + +export class CellIndex { + constructor( + public readonly columnIndex: number, + public readonly rowIndex: number, + ) {} + + toString(): string { + return `${columnIndexToString(this.columnIndex)}${rowIndexToString( + this.rowIndex, + )}`; + } + + hasRelativeIndexes(): boolean { + return this.columnIndex === LAST_INDEX || this.rowIndex === LAST_INDEX; + } + + resolveRelativeIndexes(bounds: CellIndexBounds): CellIndex { + let columnIndex = this.columnIndex; + if (columnIndex === LAST_INDEX) { + columnIndex = bounds.lastColumnIndex; + } + let rowIndex = this.rowIndex; + if (rowIndex === LAST_INDEX) { + rowIndex = bounds.lastRowIndex; + } + return new CellIndex(columnIndex, rowIndex); + } + + isInBounds(bounds: CellIndexBounds): boolean { + const columnInBounds = this.isIndexInBounds( + this.columnIndex, + bounds.lastColumnIndex, + ); + const rowInBounds = this.isIndexInBounds( + this.rowIndex, + bounds.lastRowIndex, + ); + + return columnInBounds && rowInBounds; + } + + private isIndexInBounds(index: number, max: number): boolean { + if (max < 0) { + return false; + } + return index === LAST_INDEX || index <= max; + } +} + +export function columnIndexToString(columnIndex: number): string { + if (columnIndex === LAST_INDEX) { + return '*'; + } + return columnIndexAsCharacters(columnIndex); +} + +export function rowIndexToString(rowIndex: number): string { + if (rowIndex === LAST_INDEX) { + return '*'; + } + return `${rowIndex + 1}`; +} + +export interface CellIndexBounds { + lastColumnIndex: number; + lastRowIndex: number; +} diff --git a/libs/language-server/src/lib/ast/wrappers/util/cell-range-util.ts b/libs/language-server/src/lib/ast/wrappers/util/cell-range-util.ts new file mode 100644 index 00000000..cf62d454 --- /dev/null +++ b/libs/language-server/src/lib/ast/wrappers/util/cell-range-util.ts @@ -0,0 +1,87 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; + +import { + isCellLiteral, + isColumnLiteral, + isRangeLiteral, + isRowLiteral, +} from '../../generated/ast'; +import { + CellRangeWrapper, + type CellWrapper, + type ColumnWrapper, + type RowWrapper, +} from '../cell-range-wrapper'; + +import { type CellIndex, LAST_INDEX } from './cell-index'; + +export function isCellRangeWrapper(obj: unknown): obj is CellRangeWrapper { + return obj instanceof CellRangeWrapper; +} + +export function isColumnWrapper( + cellRange: CellRangeWrapper, +): cellRange is ColumnWrapper { + if (isColumnLiteral(cellRange.astNode)) { + return true; + } + if (isRangeLiteral(cellRange.astNode)) { + return ( + cellRange.from.columnIndex === cellRange.to.columnIndex && + cellRange.from.rowIndex === 0 && + cellRange.to.rowIndex === LAST_INDEX + ); + } + return false; +} + +export function getColumnIndex(column: ColumnWrapper): number { + assert(isColumnWrapper(column)); + return column.from.columnIndex; +} + +export function isRowWrapper( + cellRange: CellRangeWrapper, +): cellRange is RowWrapper { + if (isRowLiteral(cellRange.astNode)) { + return true; + } + if (isRangeLiteral(cellRange.astNode)) { + return ( + cellRange.from.rowIndex === cellRange.to.rowIndex && + cellRange.from.columnIndex === 0 && + cellRange.to.columnIndex === LAST_INDEX + ); + } + return false; +} + +export function getRowIndex(row: RowWrapper): number { + assert(isRowWrapper(row)); + return row.from.rowIndex; +} + +export function isCellWrapper( + cellRange: CellRangeWrapper, +): cellRange is CellWrapper { + if (isCellLiteral(cellRange.astNode)) { + return true; + } + if (isRangeLiteral(cellRange.astNode)) { + return ( + cellRange.from.columnIndex === cellRange.to.columnIndex && + cellRange.from.rowIndex === cellRange.to.rowIndex + ); + } + return false; +} + +export function getCellIndex(cell: CellWrapper): CellIndex { + assert(isCellWrapper(cell)); + return cell.from; +} diff --git a/libs/language-server/src/lib/ast/wrappers/util/column-id-util.spec.ts b/libs/language-server/src/lib/ast/wrappers/util/column-id-util.spec.ts new file mode 100644 index 00000000..602eb984 --- /dev/null +++ b/libs/language-server/src/lib/ast/wrappers/util/column-id-util.spec.ts @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + columnCharactersAsIndex, + columnIndexAsCharacters, +} from './column-id-util'; + +describe('Column ID utils', () => { + const testValues = new Map<string, number>([ + ['A', 0], + ['Z', 25], + ['AA', 26], + ['BF', 57], + ['YE', 654], + ['ZZ', 701], + ]); + + for (const [letters, index] of testValues.entries()) { + it(`should convert ${letters} to ${index}`, () => { + expect(columnCharactersAsIndex(letters)).toStrictEqual(index); + }); + + it(`should convert ${index} to ${letters}`, () => { + expect(columnIndexAsCharacters(index)).toStrictEqual(letters); + }); + } +}); diff --git a/libs/language-server/src/lib/ast/wrappers/util/column-id-util.ts b/libs/language-server/src/lib/ast/wrappers/util/column-id-util.ts new file mode 100644 index 00000000..06b86f78 --- /dev/null +++ b/libs/language-server/src/lib/ast/wrappers/util/column-id-util.ts @@ -0,0 +1,40 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +const firstCharacter = 'A'; +const lastCharacter = 'Z'; +const characterOffset = firstCharacter.charCodeAt(0); +const numberOfCharacters = lastCharacter.charCodeAt(0) - characterOffset + 1; + +export function columnIndexAsCharacters(columnIndex: number): string { + let columnCharacters = ''; + let value = columnIndex + 1; + do { + const remainder = value % numberOfCharacters; + let quotient = Math.floor(value / numberOfCharacters); + if (remainder !== 0) { + columnCharacters = + String.fromCharCode(remainder + characterOffset - 1) + columnCharacters; + } else { + --quotient; + columnCharacters = lastCharacter + columnCharacters; + } + value = quotient; + } while (value > 0); + return columnCharacters; +} + +export function columnCharactersAsIndex(columnCharacters: string): number { + let columnIndex = 0; + for (let position = 0; position < columnCharacters.length; ++position) { + const charCode = columnCharacters.charCodeAt(position); + const characterIndex = charCode - characterOffset + 1; + const factor = Math.pow( + numberOfCharacters, + columnCharacters.length - 1 - position, + ); + columnIndex += characterIndex * factor; + } + return columnIndex - 1; +} diff --git a/libs/language-server/src/lib/ast/wrappers/util/index.ts b/libs/language-server/src/lib/ast/wrappers/util/index.ts new file mode 100644 index 00000000..e703e85d --- /dev/null +++ b/libs/language-server/src/lib/ast/wrappers/util/index.ts @@ -0,0 +1,8 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +export * from './column-id-util'; +export * from './cell-index'; +export * from './cell-range-util'; +export * from './value-type-util'; diff --git a/libs/language-server/src/lib/ast/wrappers/util/value-type-util.ts b/libs/language-server/src/lib/ast/wrappers/util/value-type-util.ts new file mode 100644 index 00000000..2451431c --- /dev/null +++ b/libs/language-server/src/lib/ast/wrappers/util/value-type-util.ts @@ -0,0 +1,98 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; + +import { + type AtomicValueType, + isAtomicValueType, +} from '../value-type/atomic-value-type'; +import { + type PrimitiveValueType, + isPrimitiveValueType, +} from '../value-type/primitive'; +import { type ValueType } from '../value-type/value-type'; + +type ValuetypeHierarchyStack = [PrimitiveValueType, ...AtomicValueType[]]; + +export function getValuetypeHierarchyStack( + valueType: ValueType, +): ValuetypeHierarchyStack { + if (isPrimitiveValueType(valueType)) { + return [valueType]; + } else if (isAtomicValueType(valueType)) { + const supertype = valueType.getSupertype(); + assert(supertype !== undefined); + return [...getValuetypeHierarchyStack(supertype), valueType]; + } + throw new Error( + 'Should be unreachable, encountered an unknown kind of value type', + ); +} + +export function pickCommonPrimitiveValuetype( + primitiveValuetypes: PrimitiveValueType[], +): PrimitiveValueType | undefined { + assert(primitiveValuetypes.length > 0); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + let resultingType: PrimitiveValueType = primitiveValuetypes[0]!; + for (let i = 1; i < primitiveValuetypes.length; ++i) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const currentType = primitiveValuetypes[i]!; + + if (currentType.isConvertibleTo(resultingType)) { + continue; + } + + if (resultingType.isConvertibleTo(currentType)) { + // Pick the more general type as a result + resultingType = currentType; + continue; + } + + // Unable to convert the value types into each other, so there is no common primitive value type + return undefined; + } + return resultingType; +} + +export function pickCommonAtomicValueType( + stacks: ValuetypeHierarchyStack[], +): PrimitiveValueType | AtomicValueType | undefined { + const minimumStackLength = Math.min(...stacks.map((stack) => stack.length)); + + let resultingType: PrimitiveValueType | AtomicValueType | undefined = + undefined; + for (let stackLevel = 1; stackLevel < minimumStackLength; ++stackLevel) { + const typesOfCurrentLevel: (PrimitiveValueType | AtomicValueType)[] = + stacks.map( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + (stack) => stack[stackLevel]!, + ); + + if (!areAllTypesEqual(typesOfCurrentLevel)) { + // Return the common value type of the previous level + return resultingType; + } + + // Pick any type of the current level since they are all equal + resultingType = typesOfCurrentLevel[0]; + } + return resultingType; +} + +export function areAllTypesEqual(types: ValueType[]): boolean { + for (let i = 1; i < types.length; i++) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const current = types[i - 1]!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const afterCurrent = types[i]!; + if (!current.equals(afterCurrent)) { + return false; + } + } + + return true; +} diff --git a/libs/language-server/src/lib/ast/wrappers/value-type/abstract-value-type.ts b/libs/language-server/src/lib/ast/wrappers/value-type/abstract-value-type.ts new file mode 100644 index 00000000..608de858 --- /dev/null +++ b/libs/language-server/src/lib/ast/wrappers/value-type/abstract-value-type.ts @@ -0,0 +1,68 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type DataType as PolarsDataType } from 'nodejs-polars'; + +import { type InternalValueRepresentation } from '../../expressions'; + +import { type ValueType, type ValueTypeVisitor } from './value-type'; + +export abstract class AbstractValueType<I extends InternalValueRepresentation> + implements ValueType<I> +{ + abstract acceptVisitor<R>(visitor: ValueTypeVisitor<R>): R; + + isSubtypeOf(other: ValueType): boolean { + let othersSupertype = other.getSupertype(); + while (othersSupertype !== undefined) { + if (othersSupertype === this) { + return true; + } + othersSupertype = othersSupertype.getSupertype(); + } + return false; + } + + getSupertype(): ValueType | undefined { + if (this.hasSupertypeCycle()) { + return undefined; + } + return this.doGetSupertype(); + } + + protected abstract doGetSupertype(): ValueType | undefined; + + abstract equals(target: ValueType): boolean; + + abstract isAllowedAsRuntimeParameter(): boolean; + + abstract isConvertibleTo(target: ValueType): boolean; + + isReferenceableByUser(): boolean { + return false; + } + + abstract isInternalValueRepresentation( + operandValue: InternalValueRepresentation | undefined, + ): operandValue is I; + + abstract getName(): string; + + hasSupertypeCycle(visited: ValueType[] = []): boolean { + const cycleDetected = visited.some((v) => v.equals(this)); + if (cycleDetected) { + return true; + } + visited.push(this); + + const supertype = this.doGetSupertype(); + if (supertype === undefined) { + return false; + } + + return supertype.hasSupertypeCycle(visited); + } + + abstract toPolarsDataType(): PolarsDataType | undefined; +} diff --git a/libs/language-server/src/lib/ast/wrappers/value-type/atomic-value-type.ts b/libs/language-server/src/lib/ast/wrappers/value-type/atomic-value-type.ts new file mode 100644 index 00000000..477d75de --- /dev/null +++ b/libs/language-server/src/lib/ast/wrappers/value-type/atomic-value-type.ts @@ -0,0 +1,122 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; + +import { evaluateExpression } from '../../expressions/evaluate-expression'; +import { type EvaluationContext } from '../../expressions/evaluation-context'; +import { type InternalValueRepresentation } from '../../expressions/internal-value-representation'; +import { + type ConstraintDefinition, + type ValuetypeDefinition, +} from '../../generated/ast'; +import { type AstNodeWrapper } from '../ast-node-wrapper'; +import { type WrapperFactoryProvider } from '../wrapper-factory-provider'; + +import { AbstractValueType } from './abstract-value-type'; +import { type ValueTypeProvider } from './primitive'; +import { CollectionValueType } from './primitive/collection/collection-value-type'; +import { type ValueType, type ValueTypeVisitor } from './value-type'; + +export class AtomicValueType + extends AbstractValueType<InternalValueRepresentation> + implements AstNodeWrapper<ValuetypeDefinition> +{ + constructor( + public readonly astNode: ValuetypeDefinition, + private readonly valueTypeProvider: ValueTypeProvider, + private readonly wrapperFactories: WrapperFactoryProvider, + ) { + super(); + } + + acceptVisitor<R>(visitor: ValueTypeVisitor<R>): R { + return visitor.visitAtomicValueType(this); + } + + getConstraints(context: EvaluationContext): ConstraintDefinition[] { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + const constraintCollection = this.astNode?.constraints; + assert(constraintCollection !== undefined); + const constraintCollectionType = new CollectionValueType( + this.valueTypeProvider.Primitives.Constraint, + ); + const constraints = + evaluateExpression( + constraintCollection, + context, + this.wrapperFactories, + ) ?? []; + if (!constraintCollectionType.isInternalValueRepresentation(constraints)) { + return []; + } + + return constraints; + } + + override isConvertibleTo(target: ValueType): boolean { + if (target.equals(this)) { + return true; + } + + const supertype = this.getSupertype(); + if (supertype === undefined) { + return false; + } + return supertype.isConvertibleTo(target); + } + + override isReferenceableByUser(): boolean { + const supertype = this.getSupertype(); + if (supertype === undefined) { + return false; + } + return supertype.isReferenceableByUser(); + } + + override isAllowedAsRuntimeParameter(): boolean { + const supertype = this.getSupertype(); + if (supertype === undefined) { + return false; + } + return supertype.isAllowedAsRuntimeParameter(); + } + + override getName(): string { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + return this.astNode.name ?? ''; + } + + protected override doGetSupertype(): ValueType | undefined { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + const supertype = this.astNode?.type; + return this.wrapperFactories.ValueType.wrap(supertype); + } + + override isInternalValueRepresentation( + operandValue: InternalValueRepresentation | undefined, + ): operandValue is InternalValueRepresentation { + const supertype = this.getSupertype(); + if (supertype === undefined) { + return false; + } + return supertype.isInternalValueRepresentation(operandValue); + } + + override equals(target: ValueType): boolean { + if (target instanceof AtomicValueType) { + return this.astNode === target.astNode; + } + return false; + } + + override toPolarsDataType(): undefined { + return undefined; + } +} + +export function isAtomicValueType(v: unknown): v is AtomicValueType { + return v instanceof AtomicValueType; +} diff --git a/libs/language-server/src/lib/ast/wrappers/value-type/index.ts b/libs/language-server/src/lib/ast/wrappers/value-type/index.ts new file mode 100644 index 00000000..d5f70003 --- /dev/null +++ b/libs/language-server/src/lib/ast/wrappers/value-type/index.ts @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/* + * Note: Only export types if possible to enforce usage of WrapperFactory outside this directory. + * This allows us to avoid dependency cycles between the language server and interpreter. + */ + +export { type ValueType, ValueTypeVisitor } from './value-type'; +export { type AtomicValueType, isAtomicValueType } from './atomic-value-type'; +export * from './primitive'; // type export handled one level deeper diff --git a/libs/language-server/src/lib/ast/wrappers/value-type/primitive/boolean-value-type.ts b/libs/language-server/src/lib/ast/wrappers/value-type/primitive/boolean-value-type.ts new file mode 100644 index 00000000..fb9c600a --- /dev/null +++ b/libs/language-server/src/lib/ast/wrappers/value-type/primitive/boolean-value-type.ts @@ -0,0 +1,57 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { DataType as PlDType } from 'nodejs-polars'; + +import { type InternalValueRepresentation } from '../../../expressions/internal-value-representation'; +import { type ValueTypeVisitor } from '../value-type'; + +import { PrimitiveValueType } from './primitive-value-type'; + +const TRUE_REGEX = /^true$/i; +const FALSE_REGEX = /^false$/i; + +export class BooleanValuetype extends PrimitiveValueType<boolean> { + acceptVisitor<R>(visitor: ValueTypeVisitor<R>): R { + return visitor.visitBoolean(this); + } + + override isAllowedAsRuntimeParameter(): boolean { + return true; + } + + override getName(): 'boolean' { + return 'boolean'; + } + + override isInternalValueRepresentation( + operandValue: InternalValueRepresentation | undefined, + ): operandValue is boolean { + return typeof operandValue === 'boolean'; + } + + override isReferenceableByUser() { + return true; + } + + override getUserDoc(): string { + return ` +A boolean value. +Examples: true, false +`.trim(); + } + + override fromString(s: string): boolean | undefined { + if (TRUE_REGEX.test(s)) { + return true; + } else if (FALSE_REGEX.test(s)) { + return false; + } + return undefined; + } + + override toPolarsDataType(): PlDType { + return PlDType.Bool; + } +} diff --git a/libs/language-server/src/lib/ast/wrappers/value-type/primitive/cell-range-value-type.ts b/libs/language-server/src/lib/ast/wrappers/value-type/primitive/cell-range-value-type.ts new file mode 100644 index 00000000..2318706e --- /dev/null +++ b/libs/language-server/src/lib/ast/wrappers/value-type/primitive/cell-range-value-type.ts @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type InternalValueRepresentation } from '../../../expressions/internal-value-representation'; +import { + type CellRangeLiteral, + isCellRangeLiteral, +} from '../../../generated/ast'; +import { type ValueTypeVisitor } from '../value-type'; + +import { PrimitiveValueType } from './primitive-value-type'; + +export class CellRangeValuetype extends PrimitiveValueType<CellRangeLiteral> { + acceptVisitor<R>(visitor: ValueTypeVisitor<R>): R { + return visitor.visitCellRange(this); + } + + override isAllowedAsRuntimeParameter(): boolean { + return false; + } + + override getName(): 'CellRange' { + return 'CellRange'; + } + + override isInternalValueRepresentation( + operandValue: InternalValueRepresentation | undefined, + ): operandValue is CellRangeLiteral { + return isCellRangeLiteral(operandValue); + } + + override toPolarsDataType(): undefined { + return undefined; + } +} diff --git a/libs/language-server/src/lib/ast/wrappers/value-type/primitive/collection/abstract-collection-value-type.ts b/libs/language-server/src/lib/ast/wrappers/value-type/primitive/collection/abstract-collection-value-type.ts new file mode 100644 index 00000000..85ded4e1 --- /dev/null +++ b/libs/language-server/src/lib/ast/wrappers/value-type/primitive/collection/abstract-collection-value-type.ts @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type InternalValueRepresentation } from '../../../../expressions/internal-value-representation'; +import { PrimitiveValueType } from '../primitive-value-type'; + +export type ToArray<I extends InternalValueRepresentation | undefined> = + I extends InternalValueRepresentation ? I[] : []; + +export abstract class AbstractCollectionValueType< + I extends InternalValueRepresentation | undefined, +> extends PrimitiveValueType<ToArray<I>> { + override isAllowedAsRuntimeParameter(): boolean { + return false; + } + + override toPolarsDataType(): undefined { + return undefined; + } +} diff --git a/libs/language-server/src/lib/ast/wrappers/value-type/primitive/collection/collection-value-type.ts b/libs/language-server/src/lib/ast/wrappers/value-type/primitive/collection/collection-value-type.ts new file mode 100644 index 00000000..7f844b72 --- /dev/null +++ b/libs/language-server/src/lib/ast/wrappers/value-type/primitive/collection/collection-value-type.ts @@ -0,0 +1,58 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type AtomicInternalValueRepresentation, + type InternalValueRepresentation, +} from '../../../../expressions/internal-value-representation'; +import { type ValueType, type ValueTypeVisitor } from '../../value-type'; + +import { + AbstractCollectionValueType, + type ToArray, +} from './abstract-collection-value-type'; + +export class CollectionValueType< + I extends InternalValueRepresentation = InternalValueRepresentation, +> extends AbstractCollectionValueType<I> { + constructor(public readonly elementType: ValueType<I>) { + super(); + } + + override acceptVisitor<R>(visitor: ValueTypeVisitor<R>): R { + return visitor.visitCollection(this); + } + + override getName(): string { + return `Collection<${this.elementType.getName()}>`; + } + + override equals(target: ValueType): boolean { + return ( + target instanceof CollectionValueType && + target.elementType.equals(this.elementType) + ); + } + + override isInternalValueRepresentation( + operandValue: InternalValueRepresentation | undefined, + ): operandValue is ToArray<I> { + return ( + Array.isArray(operandValue) && + operandValue.every((element) => + this.elementType.isInternalValueRepresentation(element), + ) + ); + } + + override toPolarsDataType(): undefined { + return undefined; + } +} + +export function isCollectionValueType< + I extends AtomicInternalValueRepresentation, +>(v: unknown, elementType: ValueType<I>): v is CollectionValueType<I> { + return v instanceof CollectionValueType && v.elementType.equals(elementType); +} diff --git a/libs/language-server/src/lib/ast/wrappers/value-type/primitive/collection/empty-collection-value-type.ts b/libs/language-server/src/lib/ast/wrappers/value-type/primitive/collection/empty-collection-value-type.ts new file mode 100644 index 00000000..dd49d65e --- /dev/null +++ b/libs/language-server/src/lib/ast/wrappers/value-type/primitive/collection/empty-collection-value-type.ts @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type InternalValueRepresentation } from '../../../../expressions/internal-value-representation'; +import { type ValueType, type ValueTypeVisitor } from '../../value-type'; + +import { AbstractCollectionValueType } from './abstract-collection-value-type'; +import { CollectionValueType } from './collection-value-type'; + +export class EmptyCollectionValueType extends AbstractCollectionValueType<undefined> { + override isConvertibleTo(target: ValueType): boolean { + return ( + super.isConvertibleTo(target) || target instanceof CollectionValueType + ); + } + + override acceptVisitor<R>(visitor: ValueTypeVisitor<R>): R { + return visitor.visitCollection(this); + } + + override getName(): string { + return `Collection`; + } + + override isInternalValueRepresentation( + operandValue: InternalValueRepresentation | undefined, + ): operandValue is [] { + return Array.isArray(operandValue) && operandValue.length === 0; + } + + override toPolarsDataType(): undefined { + return undefined; + } +} diff --git a/libs/language-server/src/lib/ast/wrappers/value-type/primitive/collection/index.ts b/libs/language-server/src/lib/ast/wrappers/value-type/primitive/collection/index.ts new file mode 100644 index 00000000..4c1d7f6a --- /dev/null +++ b/libs/language-server/src/lib/ast/wrappers/value-type/primitive/collection/index.ts @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/* + * Note: Only export types if possible to enforce usage of WrapperFactory outside this directory. + * This allows us to avoid dependency cycles between the language server and interpreter. + */ + +export { + type CollectionValueType, + isCollectionValueType, +} from './collection-value-type'; +export { type EmptyCollectionValueType } from './empty-collection-value-type'; diff --git a/libs/language-server/src/lib/ast/wrappers/value-type/primitive/constraint-value-type.ts b/libs/language-server/src/lib/ast/wrappers/value-type/primitive/constraint-value-type.ts new file mode 100644 index 00000000..18e76b95 --- /dev/null +++ b/libs/language-server/src/lib/ast/wrappers/value-type/primitive/constraint-value-type.ts @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type InternalValueRepresentation } from '../../../expressions/internal-value-representation'; +import { + type ConstraintDefinition, + isConstraintDefinition, +} from '../../../generated/ast'; +import { type ValueTypeVisitor } from '../value-type'; + +import { PrimitiveValueType } from './primitive-value-type'; + +export class ConstraintValuetype extends PrimitiveValueType<ConstraintDefinition> { + acceptVisitor<R>(visitor: ValueTypeVisitor<R>): R { + return visitor.visitConstraint(this); + } + + override isAllowedAsRuntimeParameter(): boolean { + return false; + } + + override getName(): 'Constraint' { + return 'Constraint'; + } + + override isInternalValueRepresentation( + operandValue: InternalValueRepresentation | undefined, + ): operandValue is ConstraintDefinition { + return isConstraintDefinition(operandValue); + } + + override toPolarsDataType(): undefined { + return undefined; + } +} diff --git a/libs/language-server/src/lib/ast/wrappers/value-type/primitive/decimal-value-type.ts b/libs/language-server/src/lib/ast/wrappers/value-type/primitive/decimal-value-type.ts new file mode 100644 index 00000000..9d8f0bd8 --- /dev/null +++ b/libs/language-server/src/lib/ast/wrappers/value-type/primitive/decimal-value-type.ts @@ -0,0 +1,59 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { DataType as PlDType } from 'nodejs-polars'; + +import { type InternalValueRepresentation } from '../../../expressions/internal-value-representation'; +import { type ValueTypeVisitor } from '../value-type'; + +import { PrimitiveValueType } from './primitive-value-type'; + +const NUMBER_REGEX = /^[+-]?([0-9]*[,.])?[0-9]+([eE][+-]?\d+)?$/; + +export function parseDecimal(s: string): number | undefined { + if (!NUMBER_REGEX.test(s)) { + return undefined; + } + + return Number.parseFloat(s.replace(',', '.')); +} + +export class DecimalValuetype extends PrimitiveValueType<number> { + acceptVisitor<R>(visitor: ValueTypeVisitor<R>): R { + return visitor.visitDecimal(this); + } + + override isAllowedAsRuntimeParameter(): boolean { + return true; + } + + override getName(): 'decimal' { + return 'decimal'; + } + + override isInternalValueRepresentation( + operandValue: InternalValueRepresentation | undefined, + ): operandValue is number { + return typeof operandValue === 'number' && Number.isFinite(operandValue); + } + + override isReferenceableByUser() { + return true; + } + + override getUserDoc(): string { + return ` +A decimal value. +Example: 3.14 +`.trim(); + } + + override fromString(s: string): number | undefined { + return parseDecimal(s); + } + + override toPolarsDataType(): PlDType { + return PlDType.Float64; + } +} diff --git a/libs/language-server/src/lib/ast/wrappers/value-type/primitive/index.ts b/libs/language-server/src/lib/ast/wrappers/value-type/primitive/index.ts new file mode 100644 index 00000000..c0158b62 --- /dev/null +++ b/libs/language-server/src/lib/ast/wrappers/value-type/primitive/index.ts @@ -0,0 +1,30 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/* + * Note: Only export types if possible to enforce usage of WrapperFactory outside this directory. + * This allows us to avoid dependency cycles between the language server and interpreter. + */ + +export { + isPrimitiveValueType, + type PrimitiveValueType, +} from './primitive-value-type'; + +export { type BooleanValuetype } from './boolean-value-type'; +export { type CellRangeValuetype } from './cell-range-value-type'; +export { type ConstraintValuetype } from './constraint-value-type'; +export { type DecimalValuetype } from './decimal-value-type'; +export { type IntegerValuetype } from './integer-value-type'; +export { type RegexValuetype } from './regex-value-type'; +export { type TextValuetype } from './text-value-type'; +export { type ValuetypeAssignmentValuetype } from './value-type-assignment-value-type'; +export { type TransformValuetype } from './transform-value-type'; + +export { + ValueTypeProvider, + PrimitiveValueTypeProvider, +} from './primitive-value-type-provider'; + +export * from './collection'; // type export handled one level deeper diff --git a/libs/language-server/src/lib/ast/wrappers/value-type/primitive/integer-value-type.ts b/libs/language-server/src/lib/ast/wrappers/value-type/primitive/integer-value-type.ts new file mode 100644 index 00000000..b2aacb09 --- /dev/null +++ b/libs/language-server/src/lib/ast/wrappers/value-type/primitive/integer-value-type.ts @@ -0,0 +1,70 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { DataType as PlDType } from 'nodejs-polars'; + +import { type InternalValueRepresentation } from '../../../expressions/internal-value-representation'; +import { type ValueType, type ValueTypeVisitor } from '../value-type'; + +import { DecimalValuetype, parseDecimal } from './decimal-value-type'; +import { PrimitiveValueType } from './primitive-value-type'; + +export class IntegerValuetype extends PrimitiveValueType<number> { + override isConvertibleTo(target: ValueType): boolean { + return super.isConvertibleTo(target) || target instanceof DecimalValuetype; + } + + acceptVisitor<R>(visitor: ValueTypeVisitor<R>): R { + return visitor.visitInteger(this); + } + + override isAllowedAsRuntimeParameter(): boolean { + return true; + } + + override getName(): 'integer' { + return 'integer'; + } + + override isInternalValueRepresentation( + operandValue: InternalValueRepresentation | undefined, + ): operandValue is number { + return typeof operandValue === 'number' && Number.isInteger(operandValue); + } + + override isReferenceableByUser() { + return true; + } + + override getUserDoc(): string { + return ` +An integer value. +Example: 3 +`.trim(); + } + override fromString(s: string): number | undefined { + /** + * Reuse decimal number parsing to capture valid scientific notation + * of integers like 5.3e3 = 5300. In contrast to decimal, if the final number + * is not a valid integer, returns undefined. + */ + const decimalNumber = parseDecimal(s); + + if (decimalNumber === undefined) { + return undefined; + } + + const integerNumber = Math.trunc(decimalNumber); + + if (decimalNumber !== integerNumber) { + return undefined; + } + + return integerNumber; + } + + override toPolarsDataType(): PlDType { + return PlDType.Int64; + } +} diff --git a/libs/language-server/src/lib/ast/wrappers/value-type/primitive/primitive-value-type-provider.ts b/libs/language-server/src/lib/ast/wrappers/value-type/primitive/primitive-value-type-provider.ts new file mode 100644 index 00000000..a3666b78 --- /dev/null +++ b/libs/language-server/src/lib/ast/wrappers/value-type/primitive/primitive-value-type-provider.ts @@ -0,0 +1,94 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type DataType as PlDType, pl } from 'nodejs-polars'; + +import { type InternalValueRepresentation } from '../../../expressions'; +import { type ValueType } from '../value-type'; + +import { BooleanValuetype } from './boolean-value-type'; +import { CellRangeValuetype } from './cell-range-value-type'; +import { CollectionValueType } from './collection/collection-value-type'; +import { EmptyCollectionValueType } from './collection/empty-collection-value-type'; +import { ConstraintValuetype } from './constraint-value-type'; +import { DecimalValuetype } from './decimal-value-type'; +import { IntegerValuetype } from './integer-value-type'; +import { type PrimitiveValueType } from './primitive-value-type'; +import { RegexValuetype } from './regex-value-type'; +import { TextValuetype } from './text-value-type'; +import { TransformValuetype } from './transform-value-type'; +import { ValuetypeAssignmentValuetype } from './value-type-assignment-value-type'; + +/** + * Should be created as singleton due to the equality comparison of primitive value types. + * Exported for testing purposes. + */ +export class ValueTypeProvider { + Primitives = new PrimitiveValueTypeProvider(); + EmptyCollection = new EmptyCollectionValueType(); + + createCollectionValueTypeOf<I extends InternalValueRepresentation>( + input: ValueType<I>, + ): CollectionValueType<I> { + return new CollectionValueType(input); + } + + fromPolarsDType(dtype: PlDType): ValueType { + if (dtype.equals(pl.Bool)) { + return this.Primitives.Boolean; + } else if (dtype.equals(pl.Float32) || dtype.equals(pl.Float64)) { + return this.Primitives.Decimal; + } else if ( + dtype.equals(pl.Int8) || + dtype.equals(pl.Int16) || + dtype.equals(pl.Int32) || + dtype.equals(pl.Int64) || + dtype.equals(pl.UInt8) || + dtype.equals(pl.UInt16) || + dtype.equals(pl.UInt32) || + dtype.equals(pl.UInt64) + ) { + return this.Primitives.Integer; + } else if (dtype.equals(pl.String) || dtype.equals(pl.Utf8)) { + return this.Primitives.Text; + } + // TODO: + // pl.Categorical + // pl.Date + // pl.DateTime + // pl.List + // pl.Null + // pl.Struct + + throw new Error(`${dtype.variant} is not supported yet`); + } +} + +export class PrimitiveValueTypeProvider { + Decimal = new DecimalValuetype(); + Boolean = new BooleanValuetype(); + Integer = new IntegerValuetype(); + Text = new TextValuetype(); + + Regex = new RegexValuetype(); + CellRange = new CellRangeValuetype(); + Constraint = new ConstraintValuetype(); + ValuetypeAssignment = new ValuetypeAssignmentValuetype(); + + Transform = new TransformValuetype(); + + getAll(): PrimitiveValueType[] { + return [ + this.Boolean, + this.Decimal, + this.Integer, + this.Text, + this.Regex, + this.CellRange, + this.Constraint, + this.ValuetypeAssignment, + this.Transform, + ]; + } +} diff --git a/libs/language-server/src/lib/ast/wrappers/value-type/primitive/primitive-value-type.ts b/libs/language-server/src/lib/ast/wrappers/value-type/primitive/primitive-value-type.ts new file mode 100644 index 00000000..2d1f6391 --- /dev/null +++ b/libs/language-server/src/lib/ast/wrappers/value-type/primitive/primitive-value-type.ts @@ -0,0 +1,45 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type InternalValueRepresentation } from '../../../expressions/internal-value-representation'; +import { AbstractValueType } from '../abstract-value-type'; +import { type ValueType } from '../value-type'; + +export abstract class PrimitiveValueType< + I extends InternalValueRepresentation = InternalValueRepresentation, +> extends AbstractValueType<I> { + constructor() { + super(); + } + + override isConvertibleTo(target: ValueType): boolean { + return target.equals(this); // Primitive value types are always singletons + } + + override equals(target: ValueType): boolean { + return target === this; + } + + protected override doGetSupertype(): undefined { + return undefined; + } + + /** + * The user documentation for the value type. + * Text only, no comment characters. + * Should be given for all user-referenceable value types @see isReferenceableByUser + */ + getUserDoc(): string | undefined { + return undefined; + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + fromString(_s: string): I | undefined { + return undefined; + } +} + +export function isPrimitiveValueType(v: unknown): v is PrimitiveValueType { + return v instanceof PrimitiveValueType; +} diff --git a/libs/language-server/src/lib/ast/wrappers/value-type/primitive/regex-value-type.ts b/libs/language-server/src/lib/ast/wrappers/value-type/primitive/regex-value-type.ts new file mode 100644 index 00000000..17310bd5 --- /dev/null +++ b/libs/language-server/src/lib/ast/wrappers/value-type/primitive/regex-value-type.ts @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type InternalValueRepresentation } from '../../../expressions/internal-value-representation'; +import { type ValueTypeVisitor } from '../value-type'; + +import { PrimitiveValueType } from './primitive-value-type'; + +export class RegexValuetype extends PrimitiveValueType<RegExp> { + acceptVisitor<R>(visitor: ValueTypeVisitor<R>): R { + return visitor.visitRegex(this); + } + + override isAllowedAsRuntimeParameter(): boolean { + return false; + } + + override getName(): 'Regex' { + return 'Regex'; + } + + override isInternalValueRepresentation( + operandValue: InternalValueRepresentation | undefined, + ): operandValue is RegExp { + return operandValue instanceof RegExp; + } + + override toPolarsDataType(): undefined { + return undefined; + } +} diff --git a/libs/language-server/src/lib/ast/wrappers/value-type/primitive/text-value-type.ts b/libs/language-server/src/lib/ast/wrappers/value-type/primitive/text-value-type.ts new file mode 100644 index 00000000..4065da00 --- /dev/null +++ b/libs/language-server/src/lib/ast/wrappers/value-type/primitive/text-value-type.ts @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { DataType as PlDType } from 'nodejs-polars'; + +import { type InternalValueRepresentation } from '../../../expressions/internal-value-representation'; +import { type ValueTypeVisitor } from '../value-type'; + +import { PrimitiveValueType } from './primitive-value-type'; + +export class TextValuetype extends PrimitiveValueType<string> { + acceptVisitor<R>(visitor: ValueTypeVisitor<R>): R { + return visitor.visitText(this); + } + + override isAllowedAsRuntimeParameter(): boolean { + return true; + } + + override getName(): 'text' { + return 'text'; + } + + override isInternalValueRepresentation( + operandValue: InternalValueRepresentation | undefined, + ): operandValue is string { + return typeof operandValue === 'string'; + } + + override isReferenceableByUser() { + return true; + } + + override getUserDoc(): string { + return ` +A text value. +Example: "Hello World" +`.trim(); + } + + override fromString(s: string): string { + return s; + } + + override toPolarsDataType(): PlDType { + return PlDType.String; + } +} diff --git a/libs/language-server/src/lib/ast/wrappers/value-type/primitive/transform-value-type.ts b/libs/language-server/src/lib/ast/wrappers/value-type/primitive/transform-value-type.ts new file mode 100644 index 00000000..aaa23e10 --- /dev/null +++ b/libs/language-server/src/lib/ast/wrappers/value-type/primitive/transform-value-type.ts @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type InternalValueRepresentation } from '../../../expressions/internal-value-representation'; +import { + type TransformDefinition, + isTransformDefinition, +} from '../../../generated/ast'; +import { type ValueTypeVisitor } from '../value-type'; + +import { PrimitiveValueType } from './primitive-value-type'; + +export class TransformValuetype extends PrimitiveValueType<TransformDefinition> { + acceptVisitor<R>(visitor: ValueTypeVisitor<R>): R { + return visitor.visitTransform(this); + } + + override isAllowedAsRuntimeParameter(): boolean { + return false; + } + + override getName(): 'Transform' { + return 'Transform'; + } + + override isInternalValueRepresentation( + operandValue: InternalValueRepresentation | undefined, + ): operandValue is TransformDefinition { + return isTransformDefinition(operandValue); + } + + override toPolarsDataType(): undefined { + return undefined; + } +} diff --git a/libs/language-server/src/lib/ast/wrappers/value-type/primitive/value-type-assignment-value-type.ts b/libs/language-server/src/lib/ast/wrappers/value-type/primitive/value-type-assignment-value-type.ts new file mode 100644 index 00000000..4df62165 --- /dev/null +++ b/libs/language-server/src/lib/ast/wrappers/value-type/primitive/value-type-assignment-value-type.ts @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type InternalValueRepresentation } from '../../../expressions/internal-value-representation'; +import { + type ValuetypeAssignment as AstValuetypeAssignment, + isValuetypeAssignment as isAstValuetypeAssignment, +} from '../../../generated/ast'; +import { type ValueTypeVisitor } from '../value-type'; + +import { PrimitiveValueType } from './primitive-value-type'; + +export class ValuetypeAssignmentValuetype extends PrimitiveValueType<AstValuetypeAssignment> { + acceptVisitor<R>(visitor: ValueTypeVisitor<R>): R { + return visitor.visitValuetypeAssignment(this); + } + + override isAllowedAsRuntimeParameter(): boolean { + return false; + } + + override getName(): 'ValuetypeAssignment' { + return 'ValuetypeAssignment'; + } + + override isInternalValueRepresentation( + operandValue: InternalValueRepresentation | undefined, + ): operandValue is AstValuetypeAssignment { + return isAstValuetypeAssignment(operandValue); + } + + override toPolarsDataType(): undefined { + return undefined; + } +} diff --git a/libs/language-server/src/lib/ast/wrappers/value-type/value-type.ts b/libs/language-server/src/lib/ast/wrappers/value-type/value-type.ts new file mode 100644 index 00000000..25a471ab --- /dev/null +++ b/libs/language-server/src/lib/ast/wrappers/value-type/value-type.ts @@ -0,0 +1,102 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type DataType as PolarsDataType } from 'nodejs-polars'; + +import { type InternalValueRepresentation } from '../../expressions/internal-value-representation'; + +import { type AtomicValueType } from './atomic-value-type'; +import { + type BooleanValuetype, + type CellRangeValuetype, + type CollectionValueType, + type ConstraintValuetype, + type DecimalValuetype, + type EmptyCollectionValueType, + type IntegerValuetype, + type RegexValuetype, + type TextValuetype, + type TransformValuetype, + type ValuetypeAssignmentValuetype, +} from './primitive'; + +export interface VisitableValueType { + acceptVisitor(visitor: ValueTypeVisitor): void; +} + +export interface ValueType< + I extends InternalValueRepresentation = InternalValueRepresentation, +> extends VisitableValueType { + acceptVisitor<R>(visitor: ValueTypeVisitor<R>): R; + + /** + * The subtype relation reflects the hierarchy of value types. + * Primitive value types are never a subtype of another value type. + * Atomic value types may form a hierarchy below a primitive value type. + */ + isSubtypeOf(other: ValueType): boolean; + + /** + * The supertype relation reflects the hierarchy of value types. + * Primitive value types never have a supertype. + * Atomic value types may have a atomic or primitive value type as supertype. + */ + getSupertype(): ValueType | undefined; + + /** + * The convertible relation reflects the ability of primitive types to + * convert into another primitive value type in a loss-less way (e.g., int to decimal). + * Atomic value types inherit (@see isSubtypeOf) the conversion behavior of their primitive value type. + */ + isConvertibleTo(target: ValueType): boolean; + + /** + * Flag if value type can be referenced by users. + * Examples: + * - Users can (not) reference a value type to extend it in a value type definition + * - Users can (not) reference a value type to parse values in the TableInterpreter block + */ + isReferenceableByUser(): boolean; + + /** + * Typeguard to validate whether a given value is in the correct internal representation of this value type. + * For example, a TextValuetype has the internal representation string. + */ + isInternalValueRepresentation( + operandValue: InternalValueRepresentation | undefined, + ): operandValue is I; + + /** + * Checks if there is a cycle in the supertype relation. + */ + hasSupertypeCycle(visited?: ValueType[]): boolean; + + isAllowedAsRuntimeParameter(): boolean; + getName(): string; + + /** + * EXPERIMENTAL: Converts this valueType to its pola.rs equivalent + */ + toPolarsDataType(): PolarsDataType | undefined; + + equals(target: ValueType): boolean; +} + +export abstract class ValueTypeVisitor<R = unknown> { + abstract visitBoolean(valueType: BooleanValuetype): R; + abstract visitDecimal(valueType: DecimalValuetype): R; + abstract visitInteger(valueType: IntegerValuetype): R; + abstract visitText(valueType: TextValuetype): R; + + abstract visitCellRange(valueType: CellRangeValuetype): R; + abstract visitRegex(valueType: RegexValuetype): R; + abstract visitConstraint(valueType: ConstraintValuetype): R; + abstract visitValuetypeAssignment(valueType: ValuetypeAssignmentValuetype): R; + abstract visitCollection( + valueType: CollectionValueType | EmptyCollectionValueType, + ): R; + abstract visitTransform(valueType: TransformValuetype): R; + + abstract visitAtomicValueType(valueType: AtomicValueType): R; +} diff --git a/libs/language-server/src/lib/ast/wrappers/wrapper-factory-provider.ts b/libs/language-server/src/lib/ast/wrappers/wrapper-factory-provider.ts new file mode 100644 index 00000000..c94d3058 --- /dev/null +++ b/libs/language-server/src/lib/ast/wrappers/wrapper-factory-provider.ts @@ -0,0 +1,398 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; + +import { + type AstNode, + type Reference, + assertUnreachable, + isReference, +} from 'langium'; + +import { type OperatorEvaluatorRegistry } from '../expressions'; +import { + type BlockTypePipeline, + type BuiltinConstrainttypeDefinition, + type CellRangeLiteral, + type CompositeBlockTypeDefinition, + type PipeDefinition, + type PipelineDefinition, + type ReferenceableBlockTypeDefinition, + type ValueTypeReference, + type ValuetypeDefinition, + isBuiltinConstrainttypeDefinition, + isReferenceableBlockTypeDefinition, + isValueTypeReference, + isValuetypeDefinition, +} from '../generated/ast'; + +import { type AstNodeWrapper } from './ast-node-wrapper'; +import { CellRangeWrapper } from './cell-range-wrapper'; +import { PipeWrapper } from './pipe-wrapper'; +import { PipelineWrapper } from './pipeline-wrapper'; +import { BlockTypeWrapper } from './typed-object/block-type-wrapper'; +import { ConstraintTypeWrapper } from './typed-object/constrainttype-wrapper'; +import { type PrimitiveValueType, type ValueType } from './value-type'; +import { AtomicValueType } from './value-type/atomic-value-type'; +import { CollectionValueType } from './value-type/primitive/collection/collection-value-type'; +import { type ValueTypeProvider } from './value-type/primitive/primitive-value-type-provider'; + +abstract class AstNodeWrapperFactory< + N extends AstNode, + W extends AstNodeWrapper<N>, +> { + abstract canWrap(toBeWrapped: N | Reference<N>): boolean; + abstract doWrap(toBeWrapped: N | Reference<N>): W; + + wrap(toBeWrapped: N | Reference<N>): W { + assert( + this.canWrap(toBeWrapped), + `AstNode ${this.getName(toBeWrapped)} cannot be wrapped`, + ); + return this.doWrap(toBeWrapped); + } + + private getName(toBeWrapped: N | Reference<N>): string { + const node = isReference(toBeWrapped) ? toBeWrapped.ref : toBeWrapped; + if (node === undefined) { + return '<unresolved reference>'; + } + if ('name' in node && typeof node.name === 'string') { + return node.name; + } + return '<unnamed ast node>'; + } +} + +export class WrapperFactoryProvider { + readonly BlockType: BlockTypeWrapperFactory; + readonly ConstraintType: ConstraintTypeWrapperFactory; + readonly Pipeline: PipelineWrapperFactory; + readonly Pipe: PipeWrapperFactory; + readonly CellRange: CellRangeWrapperFactory; + readonly TypedObject: TypedObjectWrapperFactory; + readonly ValueType: ValueTypeWrapperFactory; + + constructor( + private readonly operatorEvaluatorRegistry: OperatorEvaluatorRegistry, + private readonly primitiveValueTypeContainer: ValueTypeProvider, + ) { + this.CellRange = new CellRangeWrapperFactory(); + this.BlockType = new BlockTypeWrapperFactory( + this.operatorEvaluatorRegistry, + primitiveValueTypeContainer, + this, + ); + this.ConstraintType = new ConstraintTypeWrapperFactory( + this.operatorEvaluatorRegistry, + primitiveValueTypeContainer, + this, + ); + this.Pipe = new PipeWrapperFactory(); + this.Pipeline = new PipelineWrapperFactory(this.Pipe); + this.TypedObject = new TypedObjectWrapperFactory( + this.BlockType, + this.ConstraintType, + ); + this.ValueType = new ValueTypeWrapperFactory( + this, + primitiveValueTypeContainer, + ); + } +} + +class CellRangeWrapperFactory extends AstNodeWrapperFactory< + CellRangeLiteral, + CellRangeWrapper +> { + canWrap(toBeWrapped: CellRangeLiteral): boolean { + return CellRangeWrapper.canBeWrapped(toBeWrapped); + } + doWrap(toBeWrapped: CellRangeLiteral): CellRangeWrapper { + return new CellRangeWrapper(toBeWrapped); + } +} + +class BlockTypeWrapperFactory extends AstNodeWrapperFactory< + ReferenceableBlockTypeDefinition, + BlockTypeWrapper +> { + constructor( + private readonly operatorEvaluatorRegistry: OperatorEvaluatorRegistry, + private readonly valueTypeProvider: ValueTypeProvider, + private readonly wrapperFactories: WrapperFactoryProvider, + ) { + super(); + } + + canWrap( + toBeWrapped: + | ReferenceableBlockTypeDefinition + | Reference<ReferenceableBlockTypeDefinition>, + ): boolean { + return BlockTypeWrapper.canBeWrapped(toBeWrapped); + } + doWrap( + toBeWrapped: + | ReferenceableBlockTypeDefinition + | Reference<ReferenceableBlockTypeDefinition>, + ): BlockTypeWrapper { + return new BlockTypeWrapper( + toBeWrapped, + this.operatorEvaluatorRegistry, + this.valueTypeProvider, + this.wrapperFactories, + ); + } +} + +class ConstraintTypeWrapperFactory extends AstNodeWrapperFactory< + BuiltinConstrainttypeDefinition, + ConstraintTypeWrapper +> { + constructor( + private readonly operatorEvaluatorRegistry: OperatorEvaluatorRegistry, + private readonly valueTypeProvider: ValueTypeProvider, + private readonly wrapperFactories: WrapperFactoryProvider, + ) { + super(); + } + + canWrap( + toBeWrapped: + | BuiltinConstrainttypeDefinition + | Reference<BuiltinConstrainttypeDefinition>, + ): boolean { + return ConstraintTypeWrapper.canBeWrapped(toBeWrapped); + } + doWrap( + toBeWrapped: + | BuiltinConstrainttypeDefinition + | Reference<BuiltinConstrainttypeDefinition>, + ): ConstraintTypeWrapper { + return new ConstraintTypeWrapper( + toBeWrapped, + this.operatorEvaluatorRegistry, + this.valueTypeProvider, + this.wrapperFactories, + ); + } +} + +class PipelineWrapperFactory extends AstNodeWrapperFactory< + PipelineDefinition | CompositeBlockTypeDefinition, + PipelineWrapper<PipelineDefinition | CompositeBlockTypeDefinition> +> { + constructor(private pipeWrapperFactory: IPipeWrapperFactory) { + super(); + } + + canWrap( + toBeWrapped: PipelineDefinition | CompositeBlockTypeDefinition, + ): boolean { + return PipelineWrapper.canBeWrapped(toBeWrapped, this.pipeWrapperFactory); + } + + override wrap<T extends PipelineDefinition | CompositeBlockTypeDefinition>( // override to adjust typing + toBeWrapped: T | Reference<T>, + ): PipelineWrapper<T> { + return super.wrap(toBeWrapped) as PipelineWrapper<T>; // implementation forwards to doWrap, so typing will be correct + } + + doWrap<T extends PipelineDefinition | CompositeBlockTypeDefinition>( + toBeWrapped: T, + ): PipelineWrapper<T> { + return new PipelineWrapper(toBeWrapped, this.pipeWrapperFactory); + } +} + +export interface IPipeWrapperFactory { + canWrap( + toBeWrapped: PipeDefinition | BlockTypePipeline, + chainIndex: number, + ): boolean; + + wrap<T extends PipeDefinition | BlockTypePipeline>( + toBeWrapped: T, + chainIndex: number, + ): PipeWrapper<T>; + + doWrap<T extends PipeDefinition | BlockTypePipeline>( + toBeWrapped: T, + chainIndex: number, + ): PipeWrapper<T>; + + wrapAll<T extends PipeDefinition | BlockTypePipeline>( + toBeWrapped: T, + ): PipeWrapper<T>[]; +} + +class PipeWrapperFactory implements IPipeWrapperFactory { + // does not extend AstNodeWrapperFactory as requires argument chainIndex for wrapping + + canWrap( + toBeWrapped: PipeDefinition | BlockTypePipeline, + chainIndex: number, + ): boolean { + return PipeWrapper.canBeWrapped(toBeWrapped, chainIndex); + } + + wrap<T extends PipeDefinition | BlockTypePipeline>( + toBeWrapped: T, + chainIndex: number, + ): PipeWrapper<T> { + assert(this.canWrap(toBeWrapped, chainIndex), `Pipe cannot be wrapped`); + return this.doWrap(toBeWrapped, chainIndex); + } + + doWrap<T extends PipeDefinition | BlockTypePipeline>( + toBeWrapped: T, + chainIndex: number, + ): PipeWrapper<T> { + return new PipeWrapper(toBeWrapped, chainIndex); + } + + wrapAll<T extends PipeDefinition | BlockTypePipeline>( + toBeWrapped: T, + ): PipeWrapper<T>[] { + const result: PipeWrapper<T>[] = []; + for ( + let chainIndex = 0; + chainIndex < toBeWrapped.blocks.length - 1; + ++chainIndex + ) { + if (!this.canWrap(toBeWrapped, chainIndex)) { + continue; + } + const pipeWrapper = this.wrap(toBeWrapped, chainIndex); + result.push(pipeWrapper); + } + return result; + } +} + +class TypedObjectWrapperFactory { + // does not extend AstNodeWrapperFactory as behavior differs, e.g., no thrown error, allowing undefined as parameter + constructor( + private readonly blockTypeWrapperFactory: BlockTypeWrapperFactory, + private readonly constraintTypeWrapperFactory: ConstraintTypeWrapperFactory, + ) {} + + /** + * Creates a wrapper for the typed object. + * Returns undefined if wrapping is not possible (does not throw an error). + */ + wrap( + toBeWrapped: + | Reference<ReferenceableBlockTypeDefinition> + | Reference<BuiltinConstrainttypeDefinition> + | BuiltinConstrainttypeDefinition + | ReferenceableBlockTypeDefinition + | undefined, + ): BlockTypeWrapper | ConstraintTypeWrapper | undefined { + const type = isReference(toBeWrapped) ? toBeWrapped.ref : toBeWrapped; + if (type === undefined) { + return undefined; + } + + if (isReferenceableBlockTypeDefinition(type)) { + if (!this.blockTypeWrapperFactory.canWrap(type)) { + return undefined; + } + return this.blockTypeWrapperFactory.wrap(type); + } else if (isBuiltinConstrainttypeDefinition(type)) { + if (!this.constraintTypeWrapperFactory.canWrap(type)) { + return undefined; + } + return this.constraintTypeWrapperFactory.wrap(type); + } + assertUnreachable(type); + } +} + +class ValueTypeWrapperFactory { + constructor( + private readonly wrapperFactories: WrapperFactoryProvider, + private readonly primitiveValueTypeProvider: ValueTypeProvider, + ) {} + + wrap( + identifier: ValuetypeDefinition | ValueTypeReference | undefined, + ): ValueType | undefined { + if (identifier === undefined) { + return undefined; + } else if (isValueTypeReference(identifier)) { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + const valueTypeDefinition = identifier?.reference?.ref; + if (valueTypeDefinition?.name === 'Collection') { + return this.wrapCollection(identifier); + } + return this.wrap(valueTypeDefinition); + } else if (isValuetypeDefinition(identifier)) { + if (identifier.name === 'Collection') { + // We don't have an object representing a generic collection + return; + } + if (identifier.isBuiltin) { + return this.wrapPrimitive(identifier); + } + return new AtomicValueType( + identifier, + this.primitiveValueTypeProvider, + this.wrapperFactories, + ); + } + assertUnreachable(identifier); + } + + wrapCollection(collectionRef: ValueTypeReference): CollectionValueType { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + const collectionDefinition = collectionRef?.reference?.ref; + assert(collectionDefinition?.name === 'Collection'); + const collectionGenerics = collectionRef.genericRefs; + if (collectionGenerics.length !== 1) { + throw new Error( + "Valuetype Collection needs exactly one generic parameter to define its elements' type", + ); + } + const generic = collectionGenerics[0]; + assert(generic !== undefined); + const elementValuetype = this.wrap(generic.ref); + if (elementValuetype === undefined) { + throw new Error( + "Could not create value type for the elements' type of value type Collection", + ); + } + return new CollectionValueType(elementValuetype); + } + + wrapPrimitive( + builtinValuetype: ValuetypeDefinition, + ): PrimitiveValueType | undefined { + assert(builtinValuetype.isBuiltin); + const name = builtinValuetype.name; + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (name === undefined) { + return undefined; + } + + const matchingPrimitives = + this.primitiveValueTypeProvider.Primitives.getAll().filter( + (valueType) => valueType.getName() === name, + ); + if (matchingPrimitives.length === 0) { + throw new Error( + `Found no PrimitiveValuetype for builtin value type "${name}"`, + ); + } + if (matchingPrimitives.length > 1) { + throw new Error( + `Found multiple ambiguous PrimitiveValuetype for builtin value type "${name}"`, + ); + } + return matchingPrimitives[0]; + } +} diff --git a/libs/language-server/src/lib/builtin-library/index.ts b/libs/language-server/src/lib/builtin-library/index.ts new file mode 100644 index 00000000..c52e9c5d --- /dev/null +++ b/libs/language-server/src/lib/builtin-library/index.ts @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +export { getStdLib } from './stdlib'; +export * from './jayvee-workspace-manager'; diff --git a/libs/language-server/src/lib/builtin-library/jayvee-workspace-manager.ts b/libs/language-server/src/lib/builtin-library/jayvee-workspace-manager.ts new file mode 100644 index 00000000..f16f6cd5 --- /dev/null +++ b/libs/language-server/src/lib/builtin-library/jayvee-workspace-manager.ts @@ -0,0 +1,48 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + DefaultWorkspaceManager, + type LangiumCoreServices, + type LangiumDocument, + type LangiumDocumentFactory, + type LangiumSharedCoreServices, +} from 'langium'; +import { type WorkspaceFolder } from 'vscode-languageserver'; +import { URI } from 'vscode-uri'; + +import { getStdLib } from './stdlib'; + +export class JayveeWorkspaceManager extends DefaultWorkspaceManager { + private documentFactory: LangiumDocumentFactory; + + constructor(services: LangiumSharedCoreServices) { + super(services); + this.documentFactory = services.workspace.LangiumDocumentFactory; + } + + override async loadAdditionalDocuments( + folders: WorkspaceFolder[], + collector: (document: LangiumDocument) => void, + ): Promise<void> { + await super.loadAdditionalDocuments(folders, collector); + + Object.entries(getStdLib()).forEach(([libName, libCode]) => { + collector(this.documentFactory.fromString(libCode, URI.parse(libName))); + }); + } +} + +/** + * Initializes the workspace with all workspace folders. + * Also loads additional required files, e.g., the standard library + */ +export async function initializeWorkspace( + services: LangiumCoreServices, + workspaceFolders: WorkspaceFolder[] = [], +): Promise<void> { + await services.shared.workspace.WorkspaceManager.initializeWorkspace( + workspaceFolders, + ); +} diff --git a/libs/language-server/src/lib/builtin-library/stdlib.ts b/libs/language-server/src/lib/builtin-library/stdlib.ts new file mode 100644 index 00000000..ccbfd14c --- /dev/null +++ b/libs/language-server/src/lib/builtin-library/stdlib.ts @@ -0,0 +1,60 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { IOType, type PrimitiveValueType } from '../ast'; +import { ValueTypeProvider } from '../ast/wrappers/value-type/primitive/primitive-value-type-provider'; + +import { PartialStdLib } from './generated/partial-stdlib'; + +export function getBuiltinValuetypesLib() { + const primitiveValuetypes = new ValueTypeProvider().Primitives.getAll() // instantiation is okay here as it has no side effects: it is only parsed to string + .map(parseBuiltinValuetypeToJayvee); + + const collectionValuetype = `${parseAsComment('For internal use only.')} +builtin valuetype Collection<ElementType>;`; + + return { + 'builtin:///stdlib/builtin-value-types.jv': [ + ...primitiveValuetypes, + collectionValuetype, + ].join('\n\n'), + }; +} + +export const IOtypesLib = { + 'builtin:///stdlib/io-types.jv': Object.values(IOType) + .map((iotype) => `builtin iotype ${iotype};`) + .join('\n\n'), +}; + +export function getStdLib() { + return { + ...PartialStdLib, + ...getBuiltinValuetypesLib(), + ...IOtypesLib, + }; +} + +function parseBuiltinValuetypeToJayvee(valueType: PrimitiveValueType): string { + const lines: string[] = []; + + const userDoc = valueType.getUserDoc(); + if (userDoc !== undefined) { + lines.push(parseAsComment(userDoc)); + } + if (!valueType.isReferenceableByUser()) { + lines.push(parseAsComment('For internal use only.')); + } + lines.push(`builtin valuetype ${valueType.getName()};`); + + return lines.join('\n'); +} + +function parseAsComment(text: string, indents = 0): string { + return text + .split('\n') + .map((l) => `// ${l}`) + .map((l) => '\t'.repeat(indents) + l) + .join('\n'); +} diff --git a/libs/language-server/src/lib/docs/index.ts b/libs/language-server/src/lib/docs/index.ts new file mode 100644 index 00000000..d632e113 --- /dev/null +++ b/libs/language-server/src/lib/docs/index.ts @@ -0,0 +1,7 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +export * from './jayvee-doc-generator'; +export * from './lsp-doc-generator'; +export * from './markdown-builder'; diff --git a/libs/language-server/src/lib/docs/jayvee-doc-generator.ts b/libs/language-server/src/lib/docs/jayvee-doc-generator.ts new file mode 100644 index 00000000..06c6cb44 --- /dev/null +++ b/libs/language-server/src/lib/docs/jayvee-doc-generator.ts @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type BlockTypeWrapper, type ConstraintTypeWrapper } from '../ast'; +import { type ValueType } from '../ast/wrappers/value-type/value-type'; + +export interface JayveeBlockTypeDocGenerator { + generateBlockTypeDoc(blockType: BlockTypeWrapper): string; +} + +export interface JayveeConstraintTypeDocGenerator { + generateConstraintTypeDoc(constraintType: ConstraintTypeWrapper): string; +} + +export interface JayveeValueTypesDocGenerator { + generateValueTypesDoc(valueTypes: ValueType[]): string; +} + +export interface JayveePropertyDocGenerator { + generatePropertyDoc( + blockType: BlockTypeWrapper, + propertyName: string, + ): string | undefined; +} diff --git a/libs/language-server/src/lib/docs/lsp-doc-generator.ts b/libs/language-server/src/lib/docs/lsp-doc-generator.ts new file mode 100644 index 00000000..5d976c69 --- /dev/null +++ b/libs/language-server/src/lib/docs/lsp-doc-generator.ts @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type BlockTypeWrapper, + type ConstraintTypeWrapper, + type TypedObjectWrapper, +} from '../ast'; + +import { + type JayveeBlockTypeDocGenerator, + type JayveeConstraintTypeDocGenerator, + type JayveePropertyDocGenerator, +} from './jayvee-doc-generator'; +import { MarkdownBuilder } from './markdown-builder'; + +export class LspDocGenerator + implements + JayveeBlockTypeDocGenerator, + JayveeConstraintTypeDocGenerator, + JayveePropertyDocGenerator +{ + generateBlockTypeDoc(blockType: BlockTypeWrapper): string { + const markdownBuilder = new MarkdownBuilder(); + return markdownBuilder.line(blockType.docs.description).build(); + } + + generateConstraintTypeDoc(constraintType: ConstraintTypeWrapper): string { + const markdownBuilder = new MarkdownBuilder(); + return markdownBuilder.line(constraintType.docs.description).build(); + } + + generatePropertyDoc( + wrapper: TypedObjectWrapper, + propertyName: string, + ): string | undefined { + const markdownBuilder = new MarkdownBuilder(); + const propertySpec = wrapper.getPropertySpecification(propertyName); + if (propertySpec === undefined) { + return undefined; + } + return markdownBuilder + .line(propertySpec.docs?.description) + .newLine() + .line(propertySpec.docs?.validation) + .build(); + } +} diff --git a/libs/language-server/src/lib/docs/markdown-builder.ts b/libs/language-server/src/lib/docs/markdown-builder.ts new file mode 100644 index 00000000..925a0b96 --- /dev/null +++ b/libs/language-server/src/lib/docs/markdown-builder.ts @@ -0,0 +1,45 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +export class MarkdownBuilder { + protected markdownTextLines: string[] = []; + + private getHeaderPrefix(depth: number): string { + return '#'.repeat(depth); + } + + heading(title: string, depth: number): MarkdownBuilder { + this.markdownTextLines.push(`${this.getHeaderPrefix(depth)} ${title}`); + this.newLine(); + return this; + } + + line(text?: string): MarkdownBuilder { + if (text !== undefined) { + this.markdownTextLines.push(text); + } + return this; + } + + code(code: string, language?: string) { + return this.line('```' + (language ?? '')) + .line(code) + .line('```') + .newLine(); + } + + newLine(): MarkdownBuilder { + this.markdownTextLines.push(''); + return this; + } + + comment(comment: string): MarkdownBuilder { + this.markdownTextLines.push(`<!-- ${comment} -->`); + return this; + } + + build(): string { + return this.markdownTextLines.join('\n'); + } +} diff --git a/libs/language-server/src/lib/index.ts b/libs/language-server/src/lib/index.ts new file mode 100644 index 00000000..a6ef2067 --- /dev/null +++ b/libs/language-server/src/lib/index.ts @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +export * from './ast'; +export * from './builtin-library'; +export * from './docs'; +export * from './services'; +export * from './util'; +export * from './validation'; +export * from './lsp'; + +export * from './jayvee-module'; diff --git a/libs/language-server/src/lib/jayvee-module.ts b/libs/language-server/src/lib/jayvee-module.ts new file mode 100644 index 00000000..4a55a408 --- /dev/null +++ b/libs/language-server/src/lib/jayvee-module.ts @@ -0,0 +1,143 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type DeepPartial, type Module, inject } from 'langium'; +import { + type DefaultSharedModuleContext, + type LangiumServices, + type LangiumSharedServices, + type PartialLangiumServices, + createDefaultModule, + createDefaultSharedModule, +} from 'langium/lsp'; + +import { + DefaultOperatorEvaluatorRegistry, + DefaultOperatorTypeComputerRegistry, + type OperatorEvaluatorRegistry, + type OperatorTypeComputerRegistry, +} from './ast/expressions/operator-registry'; +import { + JayveeGeneratedModule, + JayveeGeneratedSharedModule, +} from './ast/generated/module'; +import { ValueTypeProvider } from './ast/wrappers/value-type/primitive/primitive-value-type-provider'; +import { WrapperFactoryProvider } from './ast/wrappers/wrapper-factory-provider'; +import { JayveeWorkspaceManager } from './builtin-library/jayvee-workspace-manager'; +import { JayveeValueConverter } from './jayvee-value-converter'; +import { + JayveeCompletionProvider, + JayveeFormatter, + JayveeHoverProvider, +} from './lsp'; +import { RuntimeParameterProvider } from './services/runtime-parameter-provider'; +import { JayveeValidationRegistry } from './validation/validation-registry'; + +/** + * Declaration of custom services for the Jayvee language. + * https://langium.org/docs/configuration-services/#adding-new-services + */ +export interface JayveeAddedServices { + RuntimeParameterProvider: RuntimeParameterProvider; + operators: { + TypeComputerRegistry: OperatorTypeComputerRegistry; + EvaluatorRegistry: OperatorEvaluatorRegistry; + }; + ValueTypeProvider: ValueTypeProvider; + WrapperFactories: WrapperFactoryProvider; + validation: { + ValidationRegistry: JayveeValidationRegistry; + }; +} + +/** + * Union of Langium default services and your custom services - use this as constructor parameter + * of custom service classes. + */ +export type JayveeServices = LangiumServices & JayveeAddedServices; + +export type JayveeSharedServices = LangiumSharedServices; + +/** + * Dependency injection module that overrides Langium default services and contributes the + * declared custom services. The Langium defaults can be partially specified to override only + * selected services, while the custom services must be fully specified. + */ +export const JayveeModule: Module< + JayveeServices, + PartialLangiumServices & JayveeAddedServices +> = { + parser: { + ValueConverter: () => new JayveeValueConverter(), + }, + validation: { + ValidationRegistry: (services) => new JayveeValidationRegistry(services), + }, + lsp: { + CompletionProvider: (services: JayveeServices) => + new JayveeCompletionProvider(services), + HoverProvider: (services: JayveeServices) => + new JayveeHoverProvider(services), + Formatter: () => new JayveeFormatter(), + }, + RuntimeParameterProvider: () => new RuntimeParameterProvider(), + operators: { + TypeComputerRegistry: (services) => + new DefaultOperatorTypeComputerRegistry( + services.ValueTypeProvider, + services.WrapperFactories, + ), + EvaluatorRegistry: (services) => + new DefaultOperatorEvaluatorRegistry(services.ValueTypeProvider), + }, + ValueTypeProvider: () => new ValueTypeProvider(), + WrapperFactories: (services) => + new WrapperFactoryProvider( + services.operators.EvaluatorRegistry, + services.ValueTypeProvider, + ), +}; + +export const JayveeSharedModule: Module< + JayveeSharedServices, + DeepPartial<JayveeSharedServices> +> = { + workspace: { + WorkspaceManager: (services) => new JayveeWorkspaceManager(services), + }, +}; + +/** + * Create the full set of services required by Langium. + * + * First inject the shared services by merging two modules: + * - Langium default shared services + * - Services generated by langium-cli + * + * Then inject the language-specific services by merging three modules: + * - Langium default language-specific services + * - Services generated by langium-cli + * - Services specified in this file + * + * @param context Optional module context with the LSP connection + * @returns An object wrapping the shared services and the language-specific services + */ +export function createJayveeServices(context: DefaultSharedModuleContext): { + shared: LangiumSharedServices; + Jayvee: JayveeServices; +} { + const shared = inject( + createDefaultSharedModule(context), + JayveeGeneratedSharedModule, + JayveeSharedModule, + ); + const Jayvee = inject( + createDefaultModule({ shared }), + JayveeGeneratedModule, + JayveeModule, + ); + shared.ServiceRegistry.register(Jayvee); + + return { shared, Jayvee }; +} diff --git a/libs/language-server/src/lib/jayvee-value-converter.ts b/libs/language-server/src/lib/jayvee-value-converter.ts new file mode 100644 index 00000000..40585eda --- /dev/null +++ b/libs/language-server/src/lib/jayvee-value-converter.ts @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type CstNode, + DefaultValueConverter, + type GrammarAST, + type ValueType, +} from 'langium'; + +export class JayveeValueConverter extends DefaultValueConverter { + protected override runConverter( + rule: GrammarAST.AbstractRule, + input: string, + cstNode: CstNode, + ): ValueType { + if (rule.name === 'REGEX') { + // Trim leading and trailing '/' character: + return input.substring(1, input.length - 1); + } + return super.runConverter(rule, input, cstNode); + } +} diff --git a/libs/language-server/src/lib/lsp/index.ts b/libs/language-server/src/lib/lsp/index.ts new file mode 100644 index 00000000..9d903814 --- /dev/null +++ b/libs/language-server/src/lib/lsp/index.ts @@ -0,0 +1,7 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +export * from './jayvee-completion-provider'; +export * from './jayvee-formatter'; +export * from './jayvee-hover-provider'; diff --git a/libs/language-server/src/lib/lsp/jayvee-completion-provider.ts b/libs/language-server/src/lib/lsp/jayvee-completion-provider.ts new file mode 100644 index 00000000..9f3fc325 --- /dev/null +++ b/libs/language-server/src/lib/lsp/jayvee-completion-provider.ts @@ -0,0 +1,235 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; + +import { type LangiumDocuments, type MaybePromise } from 'langium'; +import { + type CompletionAcceptor, + type CompletionContext, + type CompletionValueItem, + DefaultCompletionProvider, + type NextFeature, +} from 'langium/lsp'; +import { CompletionItemKind } from 'vscode-languageserver'; + +import { type TypedObjectWrapper, type WrapperFactoryProvider } from '../ast'; +import { + type BlockDefinition, + type ConstraintDefinition, + PropertyAssignment, + type PropertyBody, + ValueTypeReference, + isBlockDefinition, + isConstraintDefinition, + isJayveeModel, + isPropertyAssignment, + isPropertyBody, +} from '../ast/generated/ast'; +import { + getAllBuiltinBlockTypes, + getAllBuiltinConstraintTypes, +} from '../ast/model-util'; +import { LspDocGenerator } from '../docs/lsp-doc-generator'; +import { type JayveeServices } from '../jayvee-module'; + +const RIGHT_ARROW_SYMBOL = '\u{2192}'; + +export class JayveeCompletionProvider extends DefaultCompletionProvider { + protected langiumDocumentService: LangiumDocuments; + protected readonly wrapperFactories: WrapperFactoryProvider; + + constructor(services: JayveeServices) { + super(services); + this.langiumDocumentService = services.shared.workspace.LangiumDocuments; + this.wrapperFactories = services.WrapperFactories; + } + + override completionFor( + context: CompletionContext, + next: NextFeature, + acceptor: CompletionAcceptor, + ): MaybePromise<void> { + const astNode = context.node; + if (astNode !== undefined) { + const isBlockTypeCompletion = + isBlockDefinition(astNode) && next.property === 'type'; + if (isBlockTypeCompletion) { + return this.completionForBlockType(context, acceptor); + } + + const isConstraintTypeCompletion = + isConstraintDefinition(astNode) && next.property === 'type'; + if (isConstraintTypeCompletion) { + return this.completionForConstraintType(context, acceptor); + } + + const isValuetypeDefinitionCompletion = next.type === ValueTypeReference; + if (isValuetypeDefinitionCompletion) { + return this.completionForValuetype(context, acceptor); + } + + const isFirstPropertyCompletion = + isPropertyBody(astNode) && next.type === PropertyAssignment; + const isOtherPropertyCompletion = + isPropertyAssignment(astNode) && next.type === PropertyAssignment; + if (isFirstPropertyCompletion || isOtherPropertyCompletion) { + return this.completionForPropertyName(astNode, context, acceptor); + } + } + return super.completionFor(context, next, acceptor); + } + + private completionForBlockType( + context: CompletionContext, + acceptor: CompletionAcceptor, + ): MaybePromise<void> { + const blockTypes = getAllBuiltinBlockTypes( + this.langiumDocumentService, + this.wrapperFactories, + ); + blockTypes.forEach((blockType) => { + const lspDocBuilder = new LspDocGenerator(); + const markdownDoc = lspDocBuilder.generateBlockTypeDoc(blockType); + acceptor(context, { + label: blockType.type, + labelDetails: { + detail: ` ${blockType.inputType} ${RIGHT_ARROW_SYMBOL} ${blockType.outputType}`, + }, + kind: CompletionItemKind.Class, + detail: `(block type)`, + documentation: { + kind: 'markdown', + value: markdownDoc, + }, + }); + }); + } + + private completionForConstraintType( + context: CompletionContext, + acceptor: CompletionAcceptor, + ): MaybePromise<void> { + const constraintTypes = getAllBuiltinConstraintTypes( + this.langiumDocumentService, + this.wrapperFactories, + ); + constraintTypes.forEach((constraintType) => { + const lspDocBuilder = new LspDocGenerator(); + const markdownDoc = + lspDocBuilder.generateConstraintTypeDoc(constraintType); + acceptor(context, { + label: constraintType.type, + labelDetails: { + detail: ` on ${constraintType.on.getName()}`, + }, + kind: CompletionItemKind.Class, + detail: `(constraint type)`, + documentation: { + kind: 'markdown', + value: markdownDoc, + }, + }); + }); + } + + private completionForValuetype( + context: CompletionContext, + acceptor: CompletionAcceptor, + ): MaybePromise<void> { + this.langiumDocumentService.all + .map((document) => document.parseResult.value) + .forEach((parsedDocument) => { + if (!isJayveeModel(parsedDocument)) { + throw new Error('Expected parsed document to be a JayveeModel'); + } + parsedDocument.valueTypes.forEach((valueTypeDefinition) => { + const valueType = + this.wrapperFactories.ValueType.wrap(valueTypeDefinition); + if (valueType !== undefined && valueType.isReferenceableByUser()) { + acceptor(context, { + label: valueTypeDefinition.name, + kind: CompletionItemKind.Class, + detail: `(valueType)`, + }); + } + }); + }); + } + + private completionForPropertyName( + astNode: PropertyBody | PropertyAssignment, + context: CompletionContext, + acceptor: CompletionAcceptor, + ) { + let container: BlockDefinition | ConstraintDefinition; + if (isPropertyBody(astNode)) { + container = astNode.$container; + } else { + container = astNode.$container.$container; + } + + const wrapper = this.wrapperFactories.TypedObject.wrap(container.type); + if (wrapper === undefined) { + return; + } + + const presentPropertyNames = container.body.properties.map( + (attr) => attr.name, + ); + + const propertyKinds: ('optional' | 'required')[] = ['required', 'optional']; + for (const propertyKind of propertyKinds) { + const propertyNames = wrapper.getPropertyNames( + propertyKind, + presentPropertyNames, + ); + this.constructPropertyCompletionValueItems( + wrapper, + propertyNames, + propertyKind, + ).forEach((item) => acceptor(context, item)); + } + } + + private constructPropertyCompletionValueItems( + wrapper: TypedObjectWrapper, + propertyNames: string[], + kind: 'required' | 'optional', + ): CompletionValueItem[] { + return propertyNames.map((propertyName) => { + const propertySpec = wrapper.getPropertySpecification(propertyName); + assert(propertySpec !== undefined); + + const completionValueItem: CompletionValueItem = { + label: propertyName, + labelDetails: { + detail: ` ${propertySpec.type.getName()}`, + }, + kind: CompletionItemKind.Field, + detail: `(${kind} property)`, + sortText: kind === 'required' ? '1' : '2', + }; + if (propertySpec.defaultValue !== undefined) { + const defaultValueString = JSON.stringify(propertySpec.defaultValue); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + completionValueItem.labelDetails!.detail += ` = ${defaultValueString}`; + } + + const lspDocBuilder = new LspDocGenerator(); + const markdownDoc = lspDocBuilder.generatePropertyDoc( + wrapper, + propertyName, + ); + if (markdownDoc !== undefined) { + completionValueItem.documentation = { + kind: 'markdown', + value: markdownDoc, + }; + } + return completionValueItem; + }); + } +} diff --git a/libs/language-server/src/lib/lsp/jayvee-formatter.ts b/libs/language-server/src/lib/lsp/jayvee-formatter.ts new file mode 100644 index 00000000..3d7c5ee3 --- /dev/null +++ b/libs/language-server/src/lib/lsp/jayvee-formatter.ts @@ -0,0 +1,163 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type AstNode, type CstNode, isCompositeCstNode } from 'langium'; +import { + AbstractFormatter, + Formatting, + type FormattingAction, + type FormattingContext, +} from 'langium/lsp'; +import { type Range, type TextEdit } from 'vscode-languageserver-protocol'; + +import { + isBlockTypePipeline, + isCellRangeLiteral, + isPipeDefinition, +} from '../ast/generated/ast'; + +export class JayveeFormatter extends AbstractFormatter { + protected override format(node: AstNode) { + const formatter = this.getNodeFormatter(node); + this.formatParenthesis(node, '{', '}'); + this.formatParenthesis(node, '[', ']'); + + formatter.keywords(',', ':', ';').prepend(Formatting.noSpace()); + formatter.keywords(':').append(Formatting.oneSpace()); + formatter + .keywords('builtin', 'property', 'requires') + .append(Formatting.oneSpace()); + + formatter + .keywords('blocktype', 'composite', 'input', 'output') + .append(Formatting.oneSpace()); + + formatter.keywords('block').append(Formatting.oneSpace()); + formatter + .keywords('constraint', 'constrainttype') + .append(Formatting.oneSpace()); + formatter.keywords('oftype').surround(Formatting.oneSpace()); + formatter.keywords('on').surround(Formatting.oneSpace()); + + formatter.keywords('iotype').append(Formatting.oneSpace()); + formatter.keywords('valuetype').append(Formatting.oneSpace()); + formatter.keywords('constraints').append(Formatting.noSpace()); + formatter + .keywords('<', '>') + .append(Formatting.noSpace()) + .prepend(Formatting.noSpace()); + + formatter.keywords('transform', 'from', 'to').append(Formatting.oneSpace()); + + formatter + .keywords('cell', 'column', 'row', 'range') + .surround(Formatting.oneSpace()); + + formatter.keywords('pipeline').append(Formatting.oneSpace()); + if (isPipeDefinition(node) || isBlockTypePipeline(node)) { + formatter.keywords('->').prepend(Formatting.indent()); + } + + if (isCellRangeLiteral(node)) { + formatter.keywords(':').append(Formatting.noSpace({ priority: 1 })); + } + } + + private formatParenthesis(node: AstNode, start: string, end: string) { + const formatter = this.getNodeFormatter(node); + if (!isCompositeCstNode(node.$cstNode)) { + return; + } + + const openingBraces = formatter.keywords(start); + const closingBraces = formatter.keyword(end); + const interior = formatter.interior(openingBraces, closingBraces); + if (interior.nodes.length === 0) { + openingBraces + .prepend(Formatting.noIndent()) + .prepend(Formatting.oneSpace()); + closingBraces + .prepend(Formatting.noIndent()) + .prepend(Formatting.oneSpace()); + return; + } + + interior.prepend(Formatting.indent({ allowMore: true })); + openingBraces.prepend(Formatting.noIndent()).prepend(Formatting.oneSpace()); + closingBraces.prepend(Formatting.noIndent()).prepend(Formatting.newLine()); + } + + /** + * Overwrite to work around this issue: + * https://github.com/eclipse-langium/langium/issues/1351 + */ + protected override createHiddenTextEdits( + previous: CstNode | undefined, + hidden: CstNode, + formatting: FormattingAction | undefined, + context: FormattingContext, + ): TextEdit[] { + const edits: TextEdit[] = []; + + // We only format hidden nodes that are on their own line. + const startLine = hidden.range.start.line; + if (previous && previous.range.end.line === startLine) { + return edits; + } + + const startRange: Range = { + start: { + character: 0, + line: startLine, + }, + end: hidden.range.start, + }; + const hiddenStartText = context.document.getText(startRange); + const move = this.findFittingMove( + startRange, + formatting?.moves ?? [], + context, + ); + + const hiddenStartChar = this.getExistingIndentationCharacterCount( + hiddenStartText, + context, + ); + const expectedStartChar = this.getIndentationCharacterCount(context, move); + + const newStartText = (context.options.insertSpaces ? ' ' : '\t').repeat( + expectedStartChar, + ); + + // Compare exact texts instead of char numbers + // to make sure the indent config (tabs vs. spaces) is respected. + if (newStartText === hiddenStartText) { + // Don't add unnecessary edits if there is nothing to do. + return edits; + } + + const lines = hidden.text.split('\n'); + lines[0] = hiddenStartText + lines[0]; + for (let i = 0; i < lines.length; i++) { + const currentLine = startLine + i; + + // Replace the full start text, so tabs and spaces work in any case. + edits.push({ + newText: newStartText, + range: { + start: { + line: currentLine, + character: 0, + }, + end: { + line: currentLine, + character: hiddenStartChar, + }, + }, + }); + } + + return edits; + } +} diff --git a/libs/language-server/src/lib/lsp/jayvee-hover-provider.ts b/libs/language-server/src/lib/lsp/jayvee-hover-provider.ts new file mode 100644 index 00000000..e9e516f6 --- /dev/null +++ b/libs/language-server/src/lib/lsp/jayvee-hover-provider.ts @@ -0,0 +1,95 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type AstNode, type MaybePromise } from 'langium'; +import { AstNodeHoverProvider } from 'langium/lsp'; +import { type Hover } from 'vscode-languageserver-protocol'; + +import { + type BuiltinBlockTypeDefinition, + type BuiltinConstrainttypeDefinition, + type PropertyAssignment, + type WrapperFactoryProvider, + isBuiltinBlockTypeDefinition, + isBuiltinConstrainttypeDefinition, + isPropertyAssignment, +} from '../ast'; +import { LspDocGenerator } from '../docs/lsp-doc-generator'; +import { type JayveeServices } from '../jayvee-module'; + +export class JayveeHoverProvider extends AstNodeHoverProvider { + protected readonly wrapperFactories: WrapperFactoryProvider; + + constructor(services: JayveeServices) { + super(services); + this.wrapperFactories = services.WrapperFactories; + } + + override getAstNodeHoverContent( + astNode: AstNode, + ): MaybePromise<Hover | undefined> { + let doc = undefined; + if (isBuiltinBlockTypeDefinition(astNode)) { + doc = this.getBlockTypeMarkdownDoc(astNode); + } + if (isBuiltinConstrainttypeDefinition(astNode)) { + doc = this.getConstraintTypeMarkdownDoc(astNode); + } + if (isPropertyAssignment(astNode)) { + doc = this.getPropertyMarkdownDoc(astNode); + } + + if (doc === undefined) { + return undefined; + } + const hover: Hover = { + contents: { + kind: 'markdown', + value: doc, + }, + }; + return hover; + } + + private getBlockTypeMarkdownDoc( + blockTypeDefinition: BuiltinBlockTypeDefinition, + ): string | undefined { + if (!this.wrapperFactories.BlockType.canWrap(blockTypeDefinition)) { + return; + } + const blockType = this.wrapperFactories.BlockType.wrap(blockTypeDefinition); + + const lspDocBuilder = new LspDocGenerator(); + return lspDocBuilder.generateBlockTypeDoc(blockType); + } + + private getConstraintTypeMarkdownDoc( + constraintTypeDefinition: BuiltinConstrainttypeDefinition, + ): string | undefined { + if ( + !this.wrapperFactories.ConstraintType.canWrap(constraintTypeDefinition) + ) { + return; + } + const constraintType = this.wrapperFactories.ConstraintType.wrap( + constraintTypeDefinition, + ); + + const lspDocBuilder = new LspDocGenerator(); + return lspDocBuilder.generateConstraintTypeDoc(constraintType); + } + + private getPropertyMarkdownDoc( + property: PropertyAssignment, + ): string | undefined { + const container = property.$container.$container; + const wrapper = this.wrapperFactories.TypedObject.wrap(container.type); + if (wrapper === undefined) { + return; + } + + const lspDocBuilder = new LspDocGenerator(); + return lspDocBuilder.generatePropertyDoc(wrapper, property.name); + } +} diff --git a/libs/language-server/src/lib/services/index.ts b/libs/language-server/src/lib/services/index.ts new file mode 100644 index 00000000..d49eebc1 --- /dev/null +++ b/libs/language-server/src/lib/services/index.ts @@ -0,0 +1,5 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +export * from './runtime-parameter-provider'; diff --git a/libs/language-server/src/lib/services/runtime-parameter-provider.ts b/libs/language-server/src/lib/services/runtime-parameter-provider.ts new file mode 100644 index 00000000..e5ba1357 --- /dev/null +++ b/libs/language-server/src/lib/services/runtime-parameter-provider.ts @@ -0,0 +1,50 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type InternalValueRepresentation } from '../ast/expressions/internal-value-representation'; +import { type ValueType } from '../ast/wrappers/value-type/value-type'; + +export type InternalValueRepresentationParser = < + I extends InternalValueRepresentation, +>( + value: string, + valueType: ValueType<I>, +) => I | undefined; + +export class RuntimeParameterProvider { + private runtimeParameters = new Map<string, string>(); + private valueParser: InternalValueRepresentationParser | undefined = + undefined; + + setValueParser(valueParser: InternalValueRepresentationParser) { + this.valueParser = valueParser; + } + + getParsedValue<I extends InternalValueRepresentation>( + key: string, + valueType: ValueType<I>, + ): I | undefined { + const stringValue = this.getRawValue(key); + if (stringValue === undefined) { + return undefined; + } + return this.valueParser?.(stringValue, valueType); + } + + getRawValue(key: string): string | undefined { + return this.runtimeParameters.get(key); + } + + setValue(key: string, value: string) { + this.runtimeParameters.set(key, value); + } + + hasValue(key: string): boolean { + return this.runtimeParameters.has(key); + } + + getReadonlyMap(): ReadonlyMap<string, InternalValueRepresentation> { + return this.runtimeParameters; + } +} diff --git a/libs/language-server/src/lib/util/constructor-class.ts b/libs/language-server/src/lib/util/constructor-class.ts new file mode 100644 index 00000000..ecf57877 --- /dev/null +++ b/libs/language-server/src/lib/util/constructor-class.ts @@ -0,0 +1,5 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +export type ConstructorClass<T> = new () => T; diff --git a/libs/language-server/src/lib/util/index.ts b/libs/language-server/src/lib/util/index.ts new file mode 100644 index 00000000..e596cfca --- /dev/null +++ b/libs/language-server/src/lib/util/index.ts @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +export * from './constructor-class'; +export * from './registry'; diff --git a/libs/language-server/src/lib/util/registry.ts b/libs/language-server/src/lib/util/registry.ts new file mode 100644 index 00000000..4aa6bb65 --- /dev/null +++ b/libs/language-server/src/lib/util/registry.ts @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; + +export class Registry<C> { + protected readonly registry = new Map<string, C>(); + + protected register(key: string, classToRegister: C) { + assert( + !this.registry.has(key), + `Multiple keys "${key}" were registered, expected at most one register call per key`, + ); + this.registry.set(key, classToRegister); + } + + protected getAll(): C[] { + return [...this.registry.values()]; + } + + protected getAllEntries(): { key: string; value: C }[] { + return [...this.registry.entries()].map(([k, v]) => { + return { key: k, value: v }; + }); + } + + protected get(key: string): C | undefined { + return this.registry.get(key); + } + + protected clear() { + this.registry.clear(); + } +} diff --git a/libs/language-server/src/lib/validation/checks/block-definition.ts b/libs/language-server/src/lib/validation/checks/block-definition.ts new file mode 100644 index 00000000..3a176cc8 --- /dev/null +++ b/libs/language-server/src/lib/validation/checks/block-definition.ts @@ -0,0 +1,7 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +export function validateBlockDefinition(): void { + // Nothing to do +} diff --git a/libs/language-server/src/lib/validation/checks/block-type-definition.spec.ts b/libs/language-server/src/lib/validation/checks/block-type-definition.spec.ts new file mode 100644 index 00000000..5ecf4986 --- /dev/null +++ b/libs/language-server/src/lib/validation/checks/block-type-definition.spec.ts @@ -0,0 +1,202 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type AstNode, + type AstNodeLocator, + type LangiumDocument, +} from 'langium'; +import { NodeFileSystem } from 'langium/node'; +import { vi } from 'vitest'; + +import { + type BuiltinBlockTypeDefinition, + type JayveeServices, + createJayveeServices, +} from '../..'; +import { + type ParseHelperOptions, + createJayveeValidationProps, + expectNoParserAndLexerErrors, + parseHelper, + readJvTestAssetHelper, + validationAcceptorMockImpl, +} from '../../../test'; + +import { validateBlockTypeDefinition } from './block-type-definition'; + +describe('Validation of BuiltinBlockTypeDefinition', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + const validationAcceptorMock = vi.fn(validationAcceptorMockImpl); + + let locator: AstNodeLocator; + let services: JayveeServices; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../../test/assets/', + ); + + async function parseAndValidateBlockType(input: string) { + const document = await parse(input); + expectNoParserAndLexerErrors(document); + + const blockType = locator.getAstNode<BuiltinBlockTypeDefinition>( + document.parseResult.value, + 'blockTypes@0', + ) as BuiltinBlockTypeDefinition; + + validateBlockTypeDefinition( + blockType, + createJayveeValidationProps(validationAcceptorMock, services), + ); + } + + beforeAll(() => { + // Create language services + services = createJayveeServices(NodeFileSystem).Jayvee; + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + afterEach(() => { + // Reset mock + validationAcceptorMock.mockReset(); + }); + + it('should diagnose error on duplicate property in block type', async () => { + const text = readJvTestAsset( + 'builtin-block-type-definition/invalid-internal-block-type-duplicate-property.jv', + ); + + await parseAndValidateBlockType(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(2); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + `Property 'testProp' in block type 'TestBlock' is defined multiple times`, + expect.any(Object), + ); + }); + + it('should diagnose error on multiple inputs in block type', async () => { + const text = readJvTestAsset( + 'builtin-block-type-definition/invalid-internal-block-type-multiple-inputs.jv', + ); + + await parseAndValidateBlockType(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(2); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + `Found more than one input definition in block type 'TestBlock'`, + expect.any(Object), + ); + }); + + it('should diagnose error on multiple outputs in block type', async () => { + const text = readJvTestAsset( + 'builtin-block-type-definition/invalid-internal-block-type-multiple-outputs.jv', + ); + + await parseAndValidateBlockType(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(2); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + `Found more than one output definition in block type 'TestBlock'`, + expect.any(Object), + ); + }); + + it('should diagnose error on missing input', async () => { + const text = readJvTestAsset( + 'builtin-block-type-definition/invalid-internal-block-type-no-input.jv', + ); + + await parseAndValidateBlockType(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + `Found no input in block type 'TestBlock' - consider using iotype "none" if the block type consumes no input`, + expect.any(Object), + ); + }); + + it('should diagnose error on missing output', async () => { + const text = readJvTestAsset( + 'builtin-block-type-definition/invalid-internal-block-type-no-output.jv', + ); + + await parseAndValidateBlockType(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + `Found no output in block type 'TestBlock' - consider using iotype "none" if the block type produces no output`, + expect.any(Object), + ); + }); + + it('should diagnose error on neither inputs nor outputs', async () => { + const text = readJvTestAsset( + 'builtin-block-type-definition/invalid-internal-block-type-wrong-property-default-value.jv', + ); + + await parseAndValidateBlockType(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + `This default value is not compatible with value type decimal`, + expect.any(Object), + ); + }); + + it('should have no error on valid extractor block type definition (no inputs)', async () => { + const text = readJvTestAsset( + 'builtin-block-type-definition/valid-internal-block-type-extractor.jv', + ); + + await parseAndValidateBlockType(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(0); + }); + + it('should have no error on valid loader block type definition (no outputs)', async () => { + const text = readJvTestAsset( + 'builtin-block-type-definition/valid-internal-block-type-loader.jv', + ); + + await parseAndValidateBlockType(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(0); + }); + + it('should have no error on valid block type definition', async () => { + const text = readJvTestAsset( + 'builtin-block-type-definition/valid-internal-block-type-no-default-prop-values.jv', + ); + + await parseAndValidateBlockType(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(0); + }); + + it('should have no error on valid block type definition with default property value', async () => { + const text = readJvTestAsset( + 'builtin-block-type-definition/valid-internal-block-type-with-default-prop-values.jv', + ); + + await parseAndValidateBlockType(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(0); + }); +}); diff --git a/libs/language-server/src/lib/validation/checks/block-type-definition.ts b/libs/language-server/src/lib/validation/checks/block-type-definition.ts new file mode 100644 index 00000000..4bbebd1f --- /dev/null +++ b/libs/language-server/src/lib/validation/checks/block-type-definition.ts @@ -0,0 +1,201 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; + +import { + type BlockTypeProperty, + type ReferenceableBlockTypeDefinition, + evaluateExpression, +} from '../../ast'; +import { type JayveeValidationProps } from '../validation-registry'; + +export function validateBlockTypeDefinition( + blockType: ReferenceableBlockTypeDefinition, + props: JayveeValidationProps, +): void { + checkNoMultipleInputs(blockType, props); + checkNoMultipleOutputs(blockType, props); + checkOneInput(blockType, props); + checkOneOutput(blockType, props); + checkNoDuplicateProperties(blockType, props); + checkPropertiesDefaultValuesHaveCorrectType(blockType, props); +} + +function checkNoMultipleInputs( + blockType: ReferenceableBlockTypeDefinition, + props: JayveeValidationProps, +): void { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (blockType.inputs === undefined) { + return; + } + + if (blockType.inputs.length > 1) { + blockType.inputs.forEach((inputDefinition) => { + props.validationContext.accept( + 'error', + `Found more than one input definition in block type '${blockType.name}'`, + { + node: inputDefinition, + }, + ); + }); + } +} + +function checkNoMultipleOutputs( + blockType: ReferenceableBlockTypeDefinition, + props: JayveeValidationProps, +): void { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (blockType.outputs === undefined) { + return; + } + + if (blockType.outputs.length > 1) { + blockType.outputs.forEach((outputDefinition) => { + props.validationContext.accept( + 'error', + `Found more than one output definition in block type '${blockType.name}'`, + { + node: outputDefinition, + }, + ); + }); + } +} + +function checkOneInput( + blockType: ReferenceableBlockTypeDefinition, + props: JayveeValidationProps, +): void { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + const numberOfInputs = blockType.inputs?.length ?? 0; + + if (numberOfInputs < 1) { + props.validationContext.accept( + 'error', + `Found no input in block type '${blockType.name}' - consider using iotype "none" if the block type consumes no input`, + { + node: blockType, + }, + ); + } +} + +function checkOneOutput( + blockType: ReferenceableBlockTypeDefinition, + props: JayveeValidationProps, +): void { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + const numberOfOutputs = blockType.outputs?.length ?? 0; + + if (numberOfOutputs < 1) { + props.validationContext.accept( + 'error', + `Found no output in block type '${blockType.name}' - consider using iotype "none" if the block type produces no output`, + { + node: blockType, + }, + ); + } +} + +function checkNoDuplicateProperties( + blockType: ReferenceableBlockTypeDefinition, + props: JayveeValidationProps, +): void { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (blockType.properties === undefined) { + return; + } + + const propertyMap = new Map<string, BlockTypeProperty[]>(); + for (const property of blockType.properties) { + const propertyName = property.name; + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (propertyName === undefined) { + continue; + } + const trackedPropertiesWithName = propertyMap.get(propertyName) ?? []; + propertyMap.set(propertyName, [...trackedPropertiesWithName, property]); + } + + [...propertyMap.values()] + .filter((properties) => properties.length > 1) + .forEach((properties) => { + properties.forEach((property) => { + props.validationContext.accept( + 'error', + `Property '${property.name}' in block type '${blockType.name}' is defined multiple times`, + { + node: property, + property: 'name', + }, + ); + }); + }); +} + +function checkPropertiesDefaultValuesHaveCorrectType( + blockType: ReferenceableBlockTypeDefinition, + props: JayveeValidationProps, +): void { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (blockType.properties === undefined) { + return; + } + + blockType.properties + .filter((property) => property.defaultValue !== undefined) + .forEach((property) => + checkPropertyDefaultValuesHasCorrectType(property, props), + ); +} + +function checkPropertyDefaultValuesHasCorrectType( + property: BlockTypeProperty, + props: JayveeValidationProps, +): void { + const defaultValueExpression = property.defaultValue; + if (defaultValueExpression === undefined) { + return; + } + + const evaluatedExpression = evaluateExpression( + defaultValueExpression, + props.evaluationContext, + props.wrapperFactories, + props.validationContext, + ); + if (evaluatedExpression === undefined) { + props.validationContext.accept( + 'error', + `Could not evaluate this expression.`, + { + node: property, + property: 'defaultValue', + }, + ); + return; + } + + const expectedValuetype = props.wrapperFactories.ValueType.wrap( + property.valueType, + ); + assert(expectedValuetype !== undefined); + + if (!expectedValuetype.isInternalValueRepresentation(evaluatedExpression)) { + props.validationContext.accept( + 'error', + `This default value is not compatible with value type ${expectedValuetype.getName()}`, + { + node: property, + property: 'defaultValue', + }, + ); + } +} diff --git a/libs/language-server/src/lib/validation/checks/block-type-specific/property-assignment.spec.ts b/libs/language-server/src/lib/validation/checks/block-type-specific/property-assignment.spec.ts new file mode 100644 index 00000000..dec8260a --- /dev/null +++ b/libs/language-server/src/lib/validation/checks/block-type-specific/property-assignment.spec.ts @@ -0,0 +1,467 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type AstNode, + type AstNodeLocator, + type LangiumDocument, +} from 'langium'; +import { NodeFileSystem } from 'langium/node'; +import { vi } from 'vitest'; + +import { + type JayveeServices, + type PropertyBody, + type PropertySpecification, + type TypedObjectWrapper, + createJayveeServices, +} from '../../..'; +import { + type ParseHelperOptions, + createJayveeValidationProps, + expectNoParserAndLexerErrors, + parseHelper, + readJvTestAssetHelper, + validationAcceptorMockImpl, +} from '../../../../test'; + +import { checkBlockTypeSpecificProperties } from './property-assignment'; + +describe('Validation of block type specific properties', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + const validationAcceptorMock = vi.fn(validationAcceptorMockImpl); + + let locator: AstNodeLocator; + let services: JayveeServices; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../../../test/assets/', + ); + + async function parseAndValidatePropertyAssignment(input: string) { + const document = await parse(input); + expectNoParserAndLexerErrors(document); + + const propertyBody = locator.getAstNode<PropertyBody>( + document.parseResult.value, + 'pipelines@0/blocks@0/body', + ) as PropertyBody; + + const props = createJayveeValidationProps(validationAcceptorMock, services); + const wrapper = props.wrapperFactories.TypedObject.wrap( + propertyBody.$container.type, + ); + expect(wrapper).toBeDefined(); + + propertyBody.properties.forEach((propertyAssignment) => { + const propertySpec = ( + wrapper as TypedObjectWrapper + ).getPropertySpecification(propertyAssignment.name); + expect(propertySpec).toBeDefined(); + + checkBlockTypeSpecificProperties( + propertyAssignment, + propertySpec as PropertySpecification, + props, + ); + }); + } + + beforeAll(() => { + // Create language services + services = createJayveeServices(NodeFileSystem).Jayvee; + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + afterEach(() => { + // Reset mock + validationAcceptorMock.mockReset(); + }); + + describe('ArchiveInterpreter block type', () => { + it('should diagnose no error on valid archiveType parameter value', async () => { + const text = readJvTestAsset( + 'property-assignment/block-type-specific/archive-interpreter/valid-valid-archivetype-param.jv', + ); + + await parseAndValidatePropertyAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(0); + }); + + it('should diagnose error on invalid archiveType parameter value', async () => { + const text = readJvTestAsset( + 'property-assignment/block-type-specific/archive-interpreter/invalid-invalid-archivetype-param.jv', + ); + + await parseAndValidatePropertyAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + 'The value of property "archiveType" must be one of the following values: "zip", "gz"', + expect.any(Object), + ); + }); + }); + + describe('CellWriter block type', () => { + it('should diagnose error on wrong dimension for at parameter', async () => { + const text = readJvTestAsset( + 'property-assignment/block-type-specific/cell-writer/invalid-wrong-at-dimension.jv', + ); + + await parseAndValidatePropertyAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + 'The cell range needs to be one-dimensional', + expect.any(Object), + ); + }); + + it('should diagnose no error on correct dimension for at parameter', async () => { + const text = readJvTestAsset( + 'property-assignment/block-type-specific/cell-writer/valid-one-dimensional-at-value.jv', + ); + + await parseAndValidatePropertyAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(0); + }); + }); + + describe('ColumnDeleter block type', () => { + it('should diagnose error on deleting partial column', async () => { + const text = readJvTestAsset( + 'property-assignment/block-type-specific/column-deleter/invalid-partial-column-delete.jv', + ); + + await parseAndValidatePropertyAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + 'An entire column needs to be selected', + expect.any(Object), + ); + }); + + it('should diagnose no error', async () => { + const text = readJvTestAsset( + 'property-assignment/block-type-specific/column-deleter/valid-column-delete.jv', + ); + + await parseAndValidatePropertyAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(0); + }); + }); + + describe('GtfsRTInterpreter block type', () => { + it('should diagnose no error on valid entity parameter value', async () => { + const text = readJvTestAsset( + 'property-assignment/block-type-specific/gtfs-rt-interpreter/valid-valid-entity-param.jv', + ); + + await parseAndValidatePropertyAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(0); + }); + + it('should diagnose error on invalid entity parameter value', async () => { + const text = readJvTestAsset( + 'property-assignment/block-type-specific/gtfs-rt-interpreter/invalid-invalid-entity-param.jv', + ); + + await parseAndValidatePropertyAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + 'The value of property "entity" must be one of the following values: "trip_update", "alert", "vehicle"', + expect.any(Object), + ); + }); + }); + + describe('HttpExtractor block type', () => { + it('should diagnose no error on valid retries parameter value', async () => { + const text = readJvTestAsset( + 'property-assignment/block-type-specific/http-extractor/valid-valid-retries-param.jv', + ); + + await parseAndValidatePropertyAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(0); + }); + + it('should diagnose error on invalid retries parameter value', async () => { + const text = readJvTestAsset( + 'property-assignment/block-type-specific/http-extractor/invalid-invalid-retries-param.jv', + ); + + await parseAndValidatePropertyAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + 'The value of property "retries" must not be smaller than 0', + expect.any(Object), + ); + }); + + it('should diagnose no error on valid retryBackoffMilliseconds parameter value', async () => { + const text = readJvTestAsset( + 'property-assignment/block-type-specific/http-extractor/valid-valid-retryBackoffMilliseconds-param.jv', + ); + + await parseAndValidatePropertyAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(0); + }); + + it('should diagnose error on invalid retryBackoffMilliseconds parameter value', async () => { + const text = readJvTestAsset( + 'property-assignment/block-type-specific/http-extractor/invalid-invalid-retryBackoffMilliseconds-param.jv', + ); + + await parseAndValidatePropertyAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + 'The value of property "retryBackoffMilliseconds" must not be smaller than 1000', + expect.any(Object), + ); + }); + + it('should diagnose no error on valid retryBackoffStrategy parameter value', async () => { + const text = readJvTestAsset( + 'property-assignment/block-type-specific/http-extractor/valid-valid-retryBackoffStrategy-param.jv', + ); + + await parseAndValidatePropertyAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(0); + }); + + it('should diagnose error on invalid retryBackoffStrategy parameter value', async () => { + const text = readJvTestAsset( + 'property-assignment/block-type-specific/http-extractor/invalid-invalid-retryBackoffStrategy-param.jv', + ); + + await parseAndValidatePropertyAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + 'The value of property "retryBackoffStrategy" must be one of the following values: "exponential", "linear"', + expect.any(Object), + ); + }); + }); + + describe('LocalFileExtractor block type', () => { + it('should diagnose no error on valid filePath parameter value', async () => { + const text = readJvTestAsset( + 'property-assignment/block-type-specific/local-file-extractor/valid-valid-filepath-param.jv', + ); + + await parseAndValidatePropertyAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(0); + }); + + it('should diagnose error on invalid filePath parameter value', async () => { + const text = readJvTestAsset( + 'property-assignment/block-type-specific/local-file-extractor/invalid-invalid-filepath-param.jv', + ); + + await parseAndValidatePropertyAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + 'File path cannot include "..". Path traversal is restricted.', + expect.any(Object), + ); + }); + }); + + describe('RowDeleter block type', () => { + it('should diagnose error on deleting partial row', async () => { + const text = readJvTestAsset( + 'property-assignment/block-type-specific/row-deleter/invalid-partial-row-delete.jv', + ); + + await parseAndValidatePropertyAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + 'An entire row needs to be selected', + expect.any(Object), + ); + }); + + it('should diagnose no error', async () => { + const text = readJvTestAsset( + 'property-assignment/block-type-specific/row-deleter/valid-row-delete.jv', + ); + + await parseAndValidatePropertyAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(0); + }); + }); + + describe('TableInterpreter block type', () => { + it('should diagnose error on non unique column names', async () => { + const text = readJvTestAsset( + 'property-assignment/block-type-specific/table-interpreter/invalid-non-unique-column-names.jv', + ); + + await parseAndValidatePropertyAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(2); + expect(validationAcceptorMock).toHaveBeenNthCalledWith( + 1, + 'error', + 'The column name "name" needs to be unique.', + expect.any(Object), + ); + expect(validationAcceptorMock).toHaveBeenNthCalledWith( + 2, + 'error', + 'The column name "name" needs to be unique.', + expect.any(Object), + ); + }); + + it('should diagnose no error', async () => { + const text = readJvTestAsset( + 'property-assignment/block-type-specific/table-interpreter/valid-correct-table.jv', + ); + + await parseAndValidatePropertyAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(0); + }); + }); + + describe('TextFileInterpreter block type', () => { + it('should diagnose no error on valid encoding parameter value', async () => { + const text = readJvTestAsset( + 'property-assignment/block-type-specific/text-file-interpreter/valid-utf8-encoding-param.jv', + ); + + await parseAndValidatePropertyAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(0); + }); + + it('should diagnose error on invalid encoding parameter value', async () => { + const text = readJvTestAsset( + 'property-assignment/block-type-specific/text-file-interpreter/invalid-invalid-encoding-param.jv', + ); + + await parseAndValidatePropertyAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + 'The value of property "encoding" must be one of the following values: "utf8", "ibm866", "latin2", "latin3", "latin4", "cyrillic", "arabic", "greek", "hebrew", "logical", "latin6", "utf-16"', + expect.any(Object), + ); + }); + }); + + describe('TextLineDeleter block type', () => { + it('should diagnose no error on valid lines parameter value', async () => { + const text = readJvTestAsset( + 'property-assignment/block-type-specific/text-line-deleter/valid-postive-line-number.jv', + ); + + await parseAndValidatePropertyAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(0); + }); + + it('should diagnose error on invalid lines parameter value', async () => { + const text = readJvTestAsset( + 'property-assignment/block-type-specific/text-line-deleter/invalid-line-less-or-equal-zero.jv', + ); + + await parseAndValidatePropertyAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(3); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + 'Line numbers need to be greater than zero', + expect.any(Object), + ); + }); + }); + + describe('TextRangeSelector block type', () => { + it('should diagnose no error on valid lineFrom parameter value', async () => { + const text = readJvTestAsset( + 'property-assignment/block-type-specific/text-range-selector/valid-postive-lineFrom-number.jv', + ); + + await parseAndValidatePropertyAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(0); + }); + + it('should diagnose error on invalid lineFrom parameter value', async () => { + const text = readJvTestAsset( + 'property-assignment/block-type-specific/text-range-selector/invalid-lineFrom-less-or-equal-zero.jv', + ); + + await parseAndValidatePropertyAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + 'The value of property "lineFrom" must not be smaller than 1', + expect.any(Object), + ); + }); + + it('should diagnose no error on valid lineTo parameter value', async () => { + const text = readJvTestAsset( + 'property-assignment/block-type-specific/text-range-selector/valid-postive-lineTo-number.jv', + ); + + await parseAndValidatePropertyAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(0); + }); + + it('should diagnose error on invalid lineTo parameter value', async () => { + const text = readJvTestAsset( + 'property-assignment/block-type-specific/text-range-selector/invalid-lineTo-less-or-equal-zero.jv', + ); + + await parseAndValidatePropertyAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + 'The value of property "lineTo" must not be smaller than 1', + expect.any(Object), + ); + }); + }); +}); diff --git a/libs/language-server/src/lib/validation/checks/block-type-specific/property-assignment.ts b/libs/language-server/src/lib/validation/checks/block-type-specific/property-assignment.ts new file mode 100644 index 00000000..5872fcd8 --- /dev/null +++ b/libs/language-server/src/lib/validation/checks/block-type-specific/property-assignment.ts @@ -0,0 +1,419 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type InternalValueRepresentation, + type PropertyAssignment, + type PropertySpecification, + evaluatePropertyValue, + internalValueToString, + isColumnWrapper, + isRowWrapper, +} from '../../../ast'; +import { type JayveeValidationProps } from '../../validation-registry'; +import { checkUniqueNames } from '../../validation-util'; + +export function checkBlockTypeSpecificProperties( + property: PropertyAssignment, + propertySpec: PropertySpecification, + props: JayveeValidationProps, +) { + const propName = property.name; + const propValue = evaluatePropertyValue( + property, + props.evaluationContext, + props.wrapperFactories, + propertySpec.type, + ); + if (propValue === undefined) { + return; + } + + switch (property.$container.$container.type.ref?.name) { + case 'ArchiveInterpreter': + return checkArchiveInterpreterProperty( + propName, + propValue, + property, + props, + ); + case 'CellWriter': + return checkCellWriterProperty(propName, property, props); + case 'ColumnDeleter': + return checkColumnDeleterProperty(propName, property, props); + case 'GtfsRTInterpreter': + return checkGtfsRTInterpreterProperty( + propName, + propValue, + property, + props, + ); + case 'HttpExtractor': + return checkHttpExtractorProperty(propName, propValue, property, props); + case 'LocalFileExtractor': + return checkLocalFileExtractorProperty( + propName, + propValue, + property, + props, + ); + case 'RowDeleter': + return checkRowDeleterProperty(propName, property, props); + case 'TableInterpreter': + return checkTableInterpreterProperty(propName, property, props); + case 'TextFileInterpreter': + return checkTextFileInterpreterProperty( + propName, + propValue, + property, + props, + ); + case 'TextLineDeleter': + return checkTextLineDeleterProperty(propName, property, props); + case 'TextRangeSelector': + return checkTextRangeSelectorProperty( + propName, + propValue, + property, + props, + ); + default: + } +} + +function checkArchiveInterpreterProperty( + propName: string, + propValue: InternalValueRepresentation, + property: PropertyAssignment, + props: JayveeValidationProps, +) { + const allowedArchiveTypes: InternalValueRepresentation[] = ['zip', 'gz']; + if (propName === 'archiveType') { + checkPropertyValueOneOf( + propValue, + allowedArchiveTypes, + propName, + property, + props, + ); + } +} + +function checkCellWriterProperty( + propName: string, + property: PropertyAssignment, + props: JayveeValidationProps, +) { + if (propName === 'at') { + const cellRange = evaluatePropertyValue( + property, + props.evaluationContext, + props.wrapperFactories, + props.valueTypeProvider.Primitives.CellRange, + ); + if (cellRange === undefined) { + return; + } + + if (!props.wrapperFactories.CellRange.canWrap(cellRange)) { + return; + } + const cellRangeWrapper = props.wrapperFactories.CellRange.wrap(cellRange); + + if (!cellRangeWrapper.isOneDimensional()) { + props.validationContext.accept( + 'error', + 'The cell range needs to be one-dimensional', + { + node: cellRangeWrapper.astNode, + }, + ); + } + } +} + +function checkColumnDeleterProperty( + propName: string, + property: PropertyAssignment, + props: JayveeValidationProps, +) { + if (propName === 'delete') { + const cellRanges = evaluatePropertyValue( + property, + props.evaluationContext, + props.wrapperFactories, + props.valueTypeProvider.createCollectionValueTypeOf( + props.valueTypeProvider.Primitives.CellRange, + ), + ); + + cellRanges?.forEach((cellRange) => { + if (!props.wrapperFactories.CellRange.canWrap(cellRange)) { + return; + } + const cellRangeWrapper = props.wrapperFactories.CellRange.wrap(cellRange); + + if (!isColumnWrapper(cellRangeWrapper)) { + props.validationContext.accept( + 'error', + 'An entire column needs to be selected', + { + node: cellRangeWrapper.astNode, + }, + ); + } + }); + } +} + +function checkGtfsRTInterpreterProperty( + propName: string, + propValue: InternalValueRepresentation, + property: PropertyAssignment, + props: JayveeValidationProps, +) { + const allowedEntities: InternalValueRepresentation[] = [ + 'trip_update', + 'alert', + 'vehicle', + ]; + if (propName === 'entity') { + checkPropertyValueOneOf( + propValue, + allowedEntities, + propName, + property, + props, + ); + } +} + +function checkHttpExtractorProperty( + propName: string, + propValue: InternalValueRepresentation, + property: PropertyAssignment, + props: JayveeValidationProps, +) { + if (propName === 'retries') { + const minRetryValue = 0; + if (typeof propValue === 'number' && propValue < minRetryValue) { + props.validationContext.accept( + 'error', + `The value of property "${propName}" must not be smaller than ${minRetryValue}`, + { + node: property, + property: 'value', + }, + ); + } + } + if (propName === 'retryBackoffMilliseconds') { + const minBackoffValue = 1000; + if (typeof propValue === 'number' && propValue < minBackoffValue) { + props.validationContext.accept( + 'error', + `The value of property "${propName}" must not be smaller than ${minBackoffValue}`, + { + node: property, + property: 'value', + }, + ); + } + } + if (propName === 'retryBackoffStrategy') { + const allowedRetryStrategies: InternalValueRepresentation[] = [ + 'exponential', + 'linear', + ]; + checkPropertyValueOneOf( + propValue, + allowedRetryStrategies, + propName, + property, + props, + ); + } +} + +function checkLocalFileExtractorProperty( + propName: string, + propValue: InternalValueRepresentation, + property: PropertyAssignment, + props: JayveeValidationProps, +) { + if ( + propName === 'filePath' && + internalValueToString(propValue, props.wrapperFactories).includes('..') + ) { + props.validationContext.accept( + 'error', + 'File path cannot include "..". Path traversal is restricted.', + { + node: property, + property: 'value', + }, + ); + } +} + +function checkRowDeleterProperty( + propName: string, + property: PropertyAssignment, + props: JayveeValidationProps, +) { + if (propName === 'delete') { + const cellRanges = evaluatePropertyValue( + property, + props.evaluationContext, + props.wrapperFactories, + props.valueTypeProvider.createCollectionValueTypeOf( + props.valueTypeProvider.Primitives.CellRange, + ), + ); + + cellRanges?.forEach((cellRange) => { + if (!props.wrapperFactories.CellRange.canWrap(cellRange)) { + return; + } + const cellRangeWrapper = props.wrapperFactories.CellRange.wrap(cellRange); + + if (!isRowWrapper(cellRangeWrapper)) { + props.validationContext.accept( + 'error', + 'An entire row needs to be selected', + { + node: cellRangeWrapper.astNode, + }, + ); + } + }); + } +} + +function checkTableInterpreterProperty( + propName: string, + property: PropertyAssignment, + props: JayveeValidationProps, +) { + if (propName === 'columns') { + const valueTypeAssignments = evaluatePropertyValue( + property, + props.evaluationContext, + props.wrapperFactories, + props.valueTypeProvider.createCollectionValueTypeOf( + props.valueTypeProvider.Primitives.ValuetypeAssignment, + ), + ); + if (valueTypeAssignments === undefined) { + return; + } + + checkUniqueNames(valueTypeAssignments, props.validationContext, 'column'); + } +} + +function checkTextFileInterpreterProperty( + propName: string, + propValue: InternalValueRepresentation, + property: PropertyAssignment, + props: JayveeValidationProps, +) { + // https://developer.mozilla.org/en-US/docs/Web/API/Encoding_API/Encodings + const allowedEncodings: InternalValueRepresentation[] = [ + 'utf8', + 'ibm866', + 'latin2', + 'latin3', + 'latin4', + 'cyrillic', + 'arabic', + 'greek', + 'hebrew', + 'logical', + 'latin6', + 'utf-16', + ]; + if (propName === 'encoding') { + checkPropertyValueOneOf( + propValue, + allowedEncodings, + propName, + property, + props, + ); + } +} + +function checkTextLineDeleterProperty( + propName: string, + property: PropertyAssignment, + props: JayveeValidationProps, +) { + if (propName === 'lines') { + const minTextLineIndex = 1; + const lines = evaluatePropertyValue( + property, + props.evaluationContext, + props.wrapperFactories, + props.valueTypeProvider.createCollectionValueTypeOf( + props.valueTypeProvider.Primitives.Integer, + ), + ); + lines?.forEach((value, index) => { + if (value < minTextLineIndex) { + props.validationContext.accept( + 'error', + `Line numbers need to be greater than zero`, + { + node: property.value, + property: 'values', + index: index, + }, + ); + } + }); + } +} + +function checkTextRangeSelectorProperty( + propName: string, + propValue: InternalValueRepresentation, + property: PropertyAssignment, + props: JayveeValidationProps, +) { + const minLineIndex = 1; + if (propName === 'lineFrom' || propName === 'lineTo') { + if (typeof propValue === 'number' && propValue < minLineIndex) { + props.validationContext.accept( + 'error', + `The value of property "${propName}" must not be smaller than ${minLineIndex}`, + { + node: property, + property: 'value', + }, + ); + } + } +} + +function checkPropertyValueOneOf( + propValue: InternalValueRepresentation, + allowedValues: InternalValueRepresentation[], + propName: string, + property: PropertyAssignment, + props: JayveeValidationProps, +) { + if (!allowedValues.includes(propValue)) { + props.validationContext.accept( + 'error', + `The value of property "${propName}" must be one of the following values: ${allowedValues + .map((v) => `${internalValueToString(v, props.wrapperFactories)}`) + .join(', ')}`, + { + node: property, + property: 'value', + }, + ); + } +} diff --git a/libs/language-server/src/lib/validation/checks/block-type-specific/property-body.spec.ts b/libs/language-server/src/lib/validation/checks/block-type-specific/property-body.spec.ts new file mode 100644 index 00000000..b1c3783e --- /dev/null +++ b/libs/language-server/src/lib/validation/checks/block-type-specific/property-body.spec.ts @@ -0,0 +1,164 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type AstNode, + type AstNodeLocator, + type LangiumDocument, +} from 'langium'; +import { NodeFileSystem } from 'langium/node'; +import { vi } from 'vitest'; + +import { + type JayveeServices, + type PropertyBody, + createJayveeServices, +} from '../../..'; +import { + type ParseHelperOptions, + createJayveeValidationProps, + expectNoParserAndLexerErrors, + parseHelper, + readJvTestAssetHelper, + validationAcceptorMockImpl, +} from '../../../../test'; + +import { checkBlockTypeSpecificPropertyBody } from './property-body'; + +describe('Validation of block type specific property bodies', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + const validationAcceptorMock = vi.fn(validationAcceptorMockImpl); + + let locator: AstNodeLocator; + let services: JayveeServices; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../../../test/assets/', + ); + + async function parseAndValidatePropertyAssignment(input: string) { + const document = await parse(input); + expectNoParserAndLexerErrors(document); + + const propertyBody = locator.getAstNode<PropertyBody>( + document.parseResult.value, + 'pipelines@0/blocks@0/body', + ) as PropertyBody; + + const props = createJayveeValidationProps(validationAcceptorMock, services); + + const wrapper = props.wrapperFactories.TypedObject.wrap( + propertyBody.$container.type, + ); + expect(wrapper).toBeDefined(); + + checkBlockTypeSpecificPropertyBody(propertyBody, props); + } + + beforeAll(() => { + // Create language services + services = createJayveeServices(NodeFileSystem).Jayvee; + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + afterEach(() => { + // Reset mock + validationAcceptorMock.mockReset(); + }); + + describe('TextRangeSelector block type', () => { + it('should diagnose error on lineFrom > lineTo', async () => { + const text = readJvTestAsset( + 'property-body/block-type-specific/text-range-selector/invalid-lineFrom-greater-lineTo.jv', + ); + + await parseAndValidatePropertyAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(2); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + 'The lower line number needs to be smaller or equal to the upper line number', + expect.any(Object), + ); + }); + + it('should diagnose no error', async () => { + const text = readJvTestAsset( + 'property-body/block-type-specific/text-range-selector/valid-correct-range.jv', + ); + + await parseAndValidatePropertyAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(0); + }); + }); + + describe('CellWriter block type', () => { + it('should diagnose error on number of write values does not match cell range', async () => { + const text = readJvTestAsset( + 'property-body/block-type-specific/cell-writer/invalid-write-length-does-not-match-cell-range.jv', + ); + + await parseAndValidatePropertyAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(2); + expect(validationAcceptorMock).toHaveBeenNthCalledWith( + 1, + 'warning', + 'The number of values to write (3) does not match the number of cells (4)', + expect.any(Object), + ); + expect(validationAcceptorMock).toHaveBeenNthCalledWith( + 2, + 'warning', + 'The number of values to write (3) does not match the number of cells (4)', + expect.any(Object), + ); + }); + + it('should diagnose no error', async () => { + const text = readJvTestAsset( + 'property-body/block-type-specific/cell-writer/valid-range-matches-array-length.jv', + ); + + await parseAndValidatePropertyAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(0); + }); + }); + + describe('TableTransformer block type', () => { + it('should diagnose error on number of input columns do not match transform input ports', async () => { + const text = readJvTestAsset( + 'property-body/block-type-specific/table-transformer/invalid-input-columns-transform-port-missmatch.jv', + ); + + await parseAndValidatePropertyAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + 'Expected 1 columns but only got 2', + expect.any(Object), + ); + }); + + it('should diagnose no error', async () => { + const text = readJvTestAsset( + 'property-body/block-type-specific/table-transformer/valid-correct-ports.jv', + ); + + await parseAndValidatePropertyAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(0); + }); + }); +}); diff --git a/libs/language-server/src/lib/validation/checks/block-type-specific/property-body.ts b/libs/language-server/src/lib/validation/checks/block-type-specific/property-body.ts new file mode 100644 index 00000000..fbc07f6c --- /dev/null +++ b/libs/language-server/src/lib/validation/checks/block-type-specific/property-body.ts @@ -0,0 +1,174 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type PropertyBody, evaluatePropertyValue } from '../../../ast'; +import { type JayveeValidationProps } from '../../validation-registry'; + +export function checkBlockTypeSpecificPropertyBody( + propertyBody: PropertyBody, + props: JayveeValidationProps, +) { + switch (propertyBody.$container.type.ref?.name) { + case 'TextRangeSelector': + return checkTextRangeSelectorPropertyBody(propertyBody, props); + case 'CellWriter': + return checkCellWriterPropertyBody(propertyBody, props); + case 'TableTransformer': + return checkTableTransformerPropertyBody(propertyBody, props); + default: + } +} + +function checkTextRangeSelectorPropertyBody( + propertyBody: PropertyBody, + props: JayveeValidationProps, +) { + const lineFromProperty = propertyBody.properties.find( + (p) => p.name === 'lineFrom', + ); + const lineToProperty = propertyBody.properties.find( + (p) => p.name === 'lineTo', + ); + + if (lineFromProperty === undefined || lineToProperty === undefined) { + return; + } + + const lineFrom = evaluatePropertyValue( + lineFromProperty, + props.evaluationContext, + props.wrapperFactories, + props.valueTypeProvider.Primitives.Integer, + ); + const lineTo = evaluatePropertyValue( + lineToProperty, + props.evaluationContext, + props.wrapperFactories, + props.valueTypeProvider.Primitives.Integer, + ); + if (lineFrom === undefined || lineTo === undefined) { + return; + } + + if (lineFrom > lineTo) { + [lineFromProperty, lineToProperty].forEach((property) => { + props.validationContext.accept( + 'error', + 'The lower line number needs to be smaller or equal to the upper line number', + { node: property.value }, + ); + }); + } +} + +function checkCellWriterPropertyBody( + propertyBody: PropertyBody, + props: JayveeValidationProps, +) { + const writeProperty = propertyBody.properties.find((p) => p.name === 'write'); + const atProperty = propertyBody.properties.find((p) => p.name === 'at'); + + if (writeProperty === undefined || atProperty === undefined) { + return; + } + + const writeValues = evaluatePropertyValue( + writeProperty, + props.evaluationContext, + props.wrapperFactories, + props.valueTypeProvider.createCollectionValueTypeOf( + props.valueTypeProvider.Primitives.Text, + ), + ); + + const atValue = evaluatePropertyValue( + atProperty, + props.evaluationContext, + props.wrapperFactories, + props.valueTypeProvider.Primitives.CellRange, + ); + + if (writeValues === undefined || atValue === undefined) { + return; + } + + if (!props.wrapperFactories.CellRange.canWrap(atValue)) { + return; + } + const atValueWrapper = props.wrapperFactories.CellRange.wrap(atValue); + + const numberOfValuesToWrite = writeValues.length; + const numberOfCells = atValueWrapper.numberOfCells(); + + if (numberOfCells !== numberOfValuesToWrite) { + [writeProperty, atProperty].forEach((propertyNode) => { + props.validationContext.accept( + 'warning', + `The number of values to write (${numberOfValuesToWrite}) does not match the number of cells (${numberOfCells})`, + { node: propertyNode.value }, + ); + }); + } +} + +function checkTableTransformerPropertyBody( + propertyBody: PropertyBody, + props: JayveeValidationProps, +) { + checkInputColumnsMatchTransformationPorts(propertyBody, props); +} + +function checkInputColumnsMatchTransformationPorts( + propertyBody: PropertyBody, + props: JayveeValidationProps, +): void { + const useProperty = propertyBody.properties.find((x) => x.name === 'use'); + const inputColumnsProperty = propertyBody.properties.find( + (x) => x.name === 'inputColumns', + ); + + if (useProperty === undefined || inputColumnsProperty === undefined) { + return; + } + + const transform = evaluatePropertyValue( + useProperty, + props.evaluationContext, + props.wrapperFactories, + props.valueTypeProvider.Primitives.Transform, + ); + const inputColumns = evaluatePropertyValue( + inputColumnsProperty, + props.evaluationContext, + props.wrapperFactories, + props.valueTypeProvider.createCollectionValueTypeOf( + props.valueTypeProvider.Primitives.Text, + ), + ); + + if (transform === undefined || inputColumns === undefined) { + return; + } + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + const transformInputPorts = transform?.body?.ports?.filter( + (x) => x.kind === 'from', + ); + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (transformInputPorts === undefined) { + return; + } + + const numberTransformPorts = transformInputPorts.length; + const numberInputColumns = inputColumns.length; + + if (numberTransformPorts !== numberInputColumns) { + props.validationContext.accept( + 'error', + `Expected ${numberTransformPorts} columns but only got ${numberInputColumns}`, + { + node: inputColumnsProperty, + }, + ); + } +} diff --git a/libs/language-server/src/lib/validation/checks/column-id.spec.ts b/libs/language-server/src/lib/validation/checks/column-id.spec.ts new file mode 100644 index 00000000..583e1e60 --- /dev/null +++ b/libs/language-server/src/lib/validation/checks/column-id.spec.ts @@ -0,0 +1,103 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type AstNode, + type AstNodeLocator, + type LangiumDocument, +} from 'langium'; +import { NodeFileSystem } from 'langium/node'; +import { vi } from 'vitest'; + +import { + type ColumnId, + type JayveeServices, + createJayveeServices, +} from '../../../lib'; +import { + type ParseHelperOptions, + createJayveeValidationProps, + expectNoParserAndLexerErrors, + parseHelper, + readJvTestAssetHelper, + validationAcceptorMockImpl, +} from '../../../test'; + +import { validateColumnId } from './column-id'; + +describe('Validation of ColumnId', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + const validationAcceptorMock = vi.fn(validationAcceptorMockImpl); + + let locator: AstNodeLocator; + let services: JayveeServices; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../../test/assets/', + ); + + async function parseAndValidateColumnId(input: string) { + const document = await parse(input); + expectNoParserAndLexerErrors(document); + + const columnId = locator.getAstNode<ColumnId>( + document.parseResult.value, + 'pipelines@0/blocks@0/body/properties@0/value/columnId', + ) as ColumnId; + + validateColumnId( + columnId, + createJayveeValidationProps(validationAcceptorMock, services), + ); + } + + beforeAll(() => { + // Create language services + services = createJayveeServices(NodeFileSystem).Jayvee; + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + afterEach(() => { + // Reset mock + validationAcceptorMock.mockReset(); + }); + + it('should have no error if denoted with capital letter', async () => { + const text = readJvTestAsset( + 'column-id/valid-column-id-capital-letters.jv', + ); + + await parseAndValidateColumnId(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(0); + }); + + it('should have no error if denoted with *', async () => { + const text = readJvTestAsset('column-id/valid-column-id-asterix.jv'); + + await parseAndValidateColumnId(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(0); + }); + + it('should diagnose error on lower case denotion', async () => { + const text = readJvTestAsset('column-id/invalid-column-id-lower-case.jv'); + + await parseAndValidateColumnId(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + `Columns need to be denoted via capital letters or the * character`, + expect.any(Object), + ); + }); +}); diff --git a/libs/language-server/src/lib/validation/checks/column-id.ts b/libs/language-server/src/lib/validation/checks/column-id.ts new file mode 100644 index 00000000..95e15492 --- /dev/null +++ b/libs/language-server/src/lib/validation/checks/column-id.ts @@ -0,0 +1,39 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** + * See https://jvalue.github.io/jayvee/docs/dev/guides/working-with-the-ast/ for why the following ESLint rule is disabled for this file. + */ +/* eslint-disable @typescript-eslint/no-unnecessary-condition */ + +import { type ColumnId } from '../../ast/generated/ast'; +import { type JayveeValidationProps } from '../validation-registry'; + +export function validateColumnId( + columnId: ColumnId, + props: JayveeValidationProps, +): void { + checkColumnIdSyntax(columnId, props); +} + +function checkColumnIdSyntax( + columnId: ColumnId, + props: JayveeValidationProps, +): void { + if (columnId?.value === undefined || columnId?.value === '*') { + return; + } + + const columnIdRegex = /^[A-Z]+$/; + if (!columnIdRegex.test(columnId.value)) { + props.validationContext.accept( + 'error', + 'Columns need to be denoted via capital letters or the * character', + { + node: columnId, + property: 'value', + }, + ); + } +} diff --git a/libs/language-server/src/lib/validation/checks/composite-block-type-definition.spec.ts b/libs/language-server/src/lib/validation/checks/composite-block-type-definition.spec.ts new file mode 100644 index 00000000..5a4da645 --- /dev/null +++ b/libs/language-server/src/lib/validation/checks/composite-block-type-definition.spec.ts @@ -0,0 +1,165 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type AstNode, + type AstNodeLocator, + type LangiumDocument, +} from 'langium'; +import { NodeFileSystem } from 'langium/node'; +import { vi } from 'vitest'; + +import { + type CompositeBlockTypeDefinition, + type JayveeServices, + createJayveeServices, +} from '../..'; +import { + type ParseHelperOptions, + createJayveeValidationProps, + expectNoParserAndLexerErrors, + parseHelper, + readJvTestAssetHelper, + validationAcceptorMockImpl, +} from '../../../test'; + +import { validateCompositeBlockTypeDefinition } from './composite-block-type-definition'; + +describe('Validation of CompositeBlockTypeDefinition', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + const validationAcceptorMock = vi.fn(validationAcceptorMockImpl); + + let locator: AstNodeLocator; + let services: JayveeServices; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../../test/assets/', + ); + + async function parseAndValidateBlockType(input: string) { + const document = await parse(input); + expectNoParserAndLexerErrors(document); + + const blockType = locator.getAstNode<CompositeBlockTypeDefinition>( + document.parseResult.value, + 'blockTypes@0', + ) as CompositeBlockTypeDefinition; + + validateCompositeBlockTypeDefinition( + blockType, + createJayveeValidationProps(validationAcceptorMock, services), + ); + } + + beforeAll(() => { + // Create language services + services = createJayveeServices(NodeFileSystem).Jayvee; + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + afterEach(() => { + // Reset mock + validationAcceptorMock.mockReset(); + }); + + it('should diagnose error on missing pipeline in composite block type', async () => { + const text = readJvTestAsset( + 'composite-block-type-definition/invalid-composite-block-type-no-pipeline.jv', + ); + + await parseAndValidateBlockType(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + `Composite block types must define one pipeline 'TestBlock'`, + expect.any(Object), + ); + }); + + it('should diagnose error on multiple pipelines in composite block type', async () => { + const text = readJvTestAsset( + 'composite-block-type-definition/invalid-composite-block-type-multiple-pipelines.jv', + ); + + await parseAndValidateBlockType(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(2); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + `Found more than one pipeline definition in composite block type 'TestBlock'`, + expect.any(Object), + ); + }); + + it('should have no error on valid extractor block type definition', async () => { + const text = readJvTestAsset( + 'composite-block-type-definition/valid-composite-block-type-extractor.jv', + ); + + await parseAndValidateBlockType(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(0); + }); + + it('should have no error on valid extractor block type definition using recursion', async () => { + const text = readJvTestAsset( + 'composite-block-type-definition/valid-composite-block-type-recursive.jv', + ); + + await parseAndValidateBlockType(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(0); + }); + + it('should diagnose error on block as input for multiple pipes', async () => { + const text = readJvTestAsset( + 'composite-block-type-definition/invalid-block-as-multiple-pipe-inputs.jv', + ); + + await parseAndValidateBlockType(text); + + // first 2 errors for multiple pipelines in test file + expect(validationAcceptorMock).toHaveBeenCalledTimes(4); + expect(validationAcceptorMock).toHaveBeenNthCalledWith( + 3, + 'error', + 'At most one pipe can be connected to the input of a block. Currently, the following 2 blocks are connected via pipes: "BlockFrom1", "BlockFrom2"', + expect.any(Object), + ); + expect(validationAcceptorMock).toHaveBeenNthCalledWith( + 4, + 'error', + 'At most one pipe can be connected to the input of a block. Currently, the following 2 blocks are connected via pipes: "BlockFrom1", "BlockFrom2"', + expect.any(Object), + ); + }); + + it('should diagnose error on block without pipe', async () => { + const text = readJvTestAsset( + 'composite-block-type-definition/invalid-unconnected-block.jv', + ); + + await parseAndValidateBlockType(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(2); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'warning', + 'A pipe should be connected to the input of this block', + expect.any(Object), + ); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'warning', + 'A pipe should be connected to the output of this block', + expect.any(Object), + ); + }); +}); diff --git a/libs/language-server/src/lib/validation/checks/composite-block-type-definition.ts b/libs/language-server/src/lib/validation/checks/composite-block-type-definition.ts new file mode 100644 index 00000000..21eb1d90 --- /dev/null +++ b/libs/language-server/src/lib/validation/checks/composite-block-type-definition.ts @@ -0,0 +1,142 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type PipelineWrapper } from '../../ast'; +import { + type BlockDefinition, + type CompositeBlockTypeDefinition, +} from '../../ast/generated/ast'; +import { type JayveeValidationProps } from '../validation-registry'; + +import { validateBlockTypeDefinition } from './block-type-definition'; +import { checkMultipleBlockInputs } from './pipeline-definition'; + +export function validateCompositeBlockTypeDefinition( + blockType: CompositeBlockTypeDefinition, + props: JayveeValidationProps, +): void { + validateBlockTypeDefinition(blockType, props); + checkHasPipeline(blockType, props); + checkExactlyOnePipeline(blockType, props); + + checkMultipleBlockInputs(blockType, props); + checkDefinedBlocksAreUsed(blockType, props); +} + +function checkHasPipeline( + blockType: CompositeBlockTypeDefinition, + props: JayveeValidationProps, +): void { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (blockType.pipes === undefined) { + return; + } + + if (blockType.pipes.length === 0) { + props.validationContext.accept( + 'error', + `Composite block types must define one pipeline '${blockType.name}'`, + { + node: blockType, + property: 'name', + }, + ); + } +} + +function checkExactlyOnePipeline( + blockType: CompositeBlockTypeDefinition, + props: JayveeValidationProps, +): void { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (blockType.pipes === undefined) { + return; + } + + if (blockType.pipes.length > 1) { + blockType.pipes.forEach((pipe) => { + props.validationContext.accept( + 'error', + `Found more than one pipeline definition in composite block type '${blockType.name}'`, + { + node: pipe, + }, + ); + }); + } +} + +export function checkDefinedBlocksAreUsed( + blockTypeDefinition: CompositeBlockTypeDefinition, + props: JayveeValidationProps, +): void { + if (!props.wrapperFactories.Pipeline.canWrap(blockTypeDefinition)) { + return; + } + const pipelineWrapper = + props.wrapperFactories.Pipeline.wrap(blockTypeDefinition); + + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (blockTypeDefinition.blocks === undefined) { + return; + } + + const containedBlocks = blockTypeDefinition.blocks; + for (const block of containedBlocks) { + doCheckDefinedBlockIsUsed(pipelineWrapper, block, props); + } +} + +function doCheckDefinedBlockIsUsed( + pipelineWrapper: PipelineWrapper<CompositeBlockTypeDefinition>, + block: BlockDefinition, + props: JayveeValidationProps, +): void { + if ( + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + block.type === undefined || + !props.wrapperFactories.BlockType.canWrap(block.type) + ) { + return; + } + const pipes = pipelineWrapper.astNode.pipes; + + const isConnectedToInput = pipes.some( + (pipe) => + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + pipe?.blocks?.at(0)?.ref === block, + ); + if (!isConnectedToInput) { + const parents = pipelineWrapper.getParentBlocks(block); + if (parents.length === 0) { + props.validationContext.accept( + 'warning', + `A pipe should be connected to the input of this block`, + { + node: block, + property: 'name', + }, + ); + } + } + + const isConnectedToOutput = pipes.some( + (pipeline) => + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + pipeline?.blocks?.at(-1)?.ref === block, + ); + if (!isConnectedToOutput) { + const children = pipelineWrapper.getChildBlocks(block); + if (children.length === 0) { + props.validationContext.accept( + 'warning', + `A pipe should be connected to the output of this block`, + { + node: block, + property: 'name', + }, + ); + } + } +} diff --git a/libs/language-server/src/lib/validation/checks/constrainttype-specific/property-assignment.spec.ts b/libs/language-server/src/lib/validation/checks/constrainttype-specific/property-assignment.spec.ts new file mode 100644 index 00000000..89ec0fcc --- /dev/null +++ b/libs/language-server/src/lib/validation/checks/constrainttype-specific/property-assignment.spec.ts @@ -0,0 +1,123 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type AstNode, + type AstNodeLocator, + type LangiumDocument, +} from 'langium'; +import { NodeFileSystem } from 'langium/node'; +import { vi } from 'vitest'; + +import { + type JayveeServices, + type PropertyBody, + type PropertySpecification, + type TypedObjectWrapper, + createJayveeServices, +} from '../../..'; +import { + type ParseHelperOptions, + createJayveeValidationProps, + expectNoParserAndLexerErrors, + parseHelper, + readJvTestAssetHelper, + validationAcceptorMockImpl, +} from '../../../../test'; + +import { checkConstraintTypeSpecificProperties } from './property-assignment'; + +describe('Validation of constraint type specific properties', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + const validationAcceptorMock = vi.fn(validationAcceptorMockImpl); + + let locator: AstNodeLocator; + let services: JayveeServices; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../../../test/assets/', + ); + + async function parseAndValidatePropertyAssignment(input: string) { + const document = await parse(input); + expectNoParserAndLexerErrors(document); + + const propertyBody = locator.getAstNode<PropertyBody>( + document.parseResult.value, + 'constraints@0/body', + ) as PropertyBody; + + const props = createJayveeValidationProps(validationAcceptorMock, services); + + const wrapper = props.wrapperFactories.TypedObject.wrap( + propertyBody.$container.type, + ); + expect(wrapper).toBeDefined(); + + propertyBody.properties.forEach((propertyAssignment) => { + const propertySpec = ( + wrapper as TypedObjectWrapper + ).getPropertySpecification(propertyAssignment.name); + expect(propertySpec).toBeDefined(); + + checkConstraintTypeSpecificProperties( + propertyAssignment, + propertySpec as PropertySpecification, + props, + ); + }); + } + + beforeAll(() => { + // Create language services + services = createJayveeServices(NodeFileSystem).Jayvee; + + locator = services.workspace.AstNodeLocator; + + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + afterEach(() => { + // Reset mock + validationAcceptorMock.mockReset(); + }); + + describe('LengthConstraint constraint type', () => { + it('should diagnose error on min < 0', async () => { + const text = readJvTestAsset( + 'property-assignment/constrainttype-specific/length-constraint/invalid-min-negative.jv', + ); + + await parseAndValidatePropertyAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + `Bounds for "minLength" need to be equal or greater than zero`, + expect.any(Object), + ); + }); + + it('should diagnose error on max < 0', async () => { + const text = readJvTestAsset( + 'property-assignment/constrainttype-specific/length-constraint/invalid-max-negative.jv', + ); + + await parseAndValidatePropertyAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(2); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + `Bounds for "maxLength" need to be equal or greater than zero`, + expect.any(Object), + ); + }); + }); +}); diff --git a/libs/language-server/src/lib/validation/checks/constrainttype-specific/property-assignment.ts b/libs/language-server/src/lib/validation/checks/constrainttype-specific/property-assignment.ts new file mode 100644 index 00000000..58ccda73 --- /dev/null +++ b/libs/language-server/src/lib/validation/checks/constrainttype-specific/property-assignment.ts @@ -0,0 +1,71 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type PropertyAssignment, + type PropertySpecification, + evaluatePropertyValue, +} from '../../../ast'; +import { type JayveeValidationProps } from '../../validation-registry'; + +export function checkConstraintTypeSpecificProperties( + property: PropertyAssignment, + propertySpec: PropertySpecification, + props: JayveeValidationProps, +) { + const propName = property.name; + const propValue = evaluatePropertyValue( + property, + props.evaluationContext, + props.wrapperFactories, + propertySpec.type, + ); + if (propValue === undefined) { + return; + } + + switch (property.$container.$container.type.ref?.name) { + case 'LengthConstraint': + return checkLengthConstraintProperty(propName, property, props); + default: + } +} + +function checkLengthConstraintProperty( + propName: string, + property: PropertyAssignment, + props: JayveeValidationProps, +) { + if (propName === 'minLength') { + checkNonNegative(property, props); + } + if (propName === 'maxLength') { + checkNonNegative(property, props); + } +} + +function checkNonNegative( + property: PropertyAssignment, + props: JayveeValidationProps, +) { + const value = evaluatePropertyValue( + property, + props.evaluationContext, + props.wrapperFactories, + props.valueTypeProvider.Primitives.Integer, + ); + if (value === undefined) { + return; + } + + if (value < 0) { + props.validationContext.accept( + 'error', + `Bounds for "${property.name}" need to be equal or greater than zero`, + { + node: property.value, + }, + ); + } +} diff --git a/libs/language-server/src/lib/validation/checks/constrainttype-specific/property-body.spec.ts b/libs/language-server/src/lib/validation/checks/constrainttype-specific/property-body.spec.ts new file mode 100644 index 00000000..8ef5ab0a --- /dev/null +++ b/libs/language-server/src/lib/validation/checks/constrainttype-specific/property-body.spec.ts @@ -0,0 +1,157 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type AstNode, + type AstNodeLocator, + type LangiumDocument, +} from 'langium'; +import { NodeFileSystem } from 'langium/node'; +import { vi } from 'vitest'; + +import { + type JayveeServices, + type PropertyBody, + createJayveeServices, +} from '../../..'; +import { + type ParseHelperOptions, + createJayveeValidationProps, + expectNoParserAndLexerErrors, + parseHelper, + readJvTestAssetHelper, + validationAcceptorMockImpl, +} from '../../../../test'; + +import { checkConstraintTypeSpecificPropertyBody } from './property-body'; + +describe('Validation of constraint type specific property bodies', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + const validationAcceptorMock = vi.fn(validationAcceptorMockImpl); + + let locator: AstNodeLocator; + let services: JayveeServices; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../../../test/assets/', + ); + + async function parseAndValidatePropertyAssignment(input: string) { + const document = await parse(input); + expectNoParserAndLexerErrors(document); + + const propertyBody = locator.getAstNode<PropertyBody>( + document.parseResult.value, + 'constraints@0/body', + ) as PropertyBody; + + const props = createJayveeValidationProps(validationAcceptorMock, services); + + const wrapper = props.wrapperFactories.TypedObject.wrap( + propertyBody.$container.type, + ); + expect(wrapper).toBeDefined(); + + checkConstraintTypeSpecificPropertyBody(propertyBody, props); + } + + beforeAll(() => { + // Create language services + services = createJayveeServices(NodeFileSystem).Jayvee; + + locator = services.workspace.AstNodeLocator; + + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + afterEach(() => { + // Reset mock + validationAcceptorMock.mockReset(); + }); + + describe('LengthConstraint constraint type', () => { + it('should diagnose error on min > max', async () => { + const text = readJvTestAsset( + 'property-body/constrainttype-specific/length-constraint/invalid-min-greater-max.jv', + ); + + await parseAndValidatePropertyAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(2); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + `The minimum length needs to be smaller or equal to the maximum length`, + expect.any(Object), + ); + }); + + it('should hint if no bounds were specified', async () => { + const text = readJvTestAsset( + 'property-body/constrainttype-specific/length-constraint/no-bound.jv', + ); + + await parseAndValidatePropertyAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'hint', + 'This constraint should either specify an upper or lower bound, otherwise it has no effect.', + expect.any(Object), + ); + }); + }); + + describe('RangeConstraint constraint type', () => { + it('should diagnose error on lower bound > upper bound', async () => { + const text = readJvTestAsset( + 'property-body/constrainttype-specific/range-constraint/invalid-lower-above-upper.jv', + ); + + await parseAndValidatePropertyAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(2); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + `The lower bound needs to be smaller or equal to the upper bound`, + expect.any(Object), + ); + }); + + it('should diagnose error on lower bound = upper bound without bound inclusivity = true', async () => { + const text = readJvTestAsset( + 'property-body/constrainttype-specific/range-constraint/invalid-missing-bound-inclusivity.jv', + ); + + await parseAndValidatePropertyAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + `Lower and upper bounds need to be inclusive if they are identical`, + expect.any(Object), + ); + }); + + it('should hint if no bounds were specified', async () => { + const text = readJvTestAsset( + 'property-body/constrainttype-specific/range-constraint/no-bound.jv', + ); + + await parseAndValidatePropertyAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'hint', + 'This constraint should either specify an upper or lower bound, otherwise it has no effect.', + expect.any(Object), + ); + }); + }); +}); diff --git a/libs/language-server/src/lib/validation/checks/constrainttype-specific/property-body.ts b/libs/language-server/src/lib/validation/checks/constrainttype-specific/property-body.ts new file mode 100644 index 00000000..36783dd6 --- /dev/null +++ b/libs/language-server/src/lib/validation/checks/constrainttype-specific/property-body.ts @@ -0,0 +1,172 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type PropertyBody, evaluatePropertyValue } from '../../../ast'; +import { type JayveeValidationProps } from '../../validation-registry'; + +export function checkConstraintTypeSpecificPropertyBody( + propertyBody: PropertyBody, + props: JayveeValidationProps, +) { + switch (propertyBody.$container.type.ref?.name) { + case 'LengthConstraint': + return checkLengthConstraintPropertyBody(propertyBody, props); + case 'RangeConstraint': + return checkRangeConstraintPropertyBody(propertyBody, props); + default: + } +} + +function checkLengthConstraintPropertyBody( + propertyBody: PropertyBody, + props: JayveeValidationProps, +) { + const minLengthProperty = propertyBody.properties.find( + (p) => p.name === 'minLength', + ); + const maxLengthProperty = propertyBody.properties.find( + (p) => p.name === 'maxLength', + ); + + if (minLengthProperty === undefined && maxLengthProperty === undefined) { + props.validationContext.accept( + 'hint', + 'This constraint should either specify an upper or lower bound, otherwise it has no effect.', + { node: propertyBody }, + ); + } + + if (minLengthProperty === undefined || maxLengthProperty === undefined) { + return; + } + + const minLength = evaluatePropertyValue( + minLengthProperty, + props.evaluationContext, + props.wrapperFactories, + props.valueTypeProvider.Primitives.Integer, + ); + const maxLength = evaluatePropertyValue( + maxLengthProperty, + props.evaluationContext, + props.wrapperFactories, + props.valueTypeProvider.Primitives.Integer, + ); + if (minLength === undefined || maxLength === undefined) { + return; + } + + if (minLength > maxLength) { + [minLengthProperty, maxLengthProperty].forEach((property) => { + props.validationContext.accept( + 'error', + 'The minimum length needs to be smaller or equal to the maximum length', + { node: property.value }, + ); + }); + } +} + +function checkRangeConstraintPropertyBody( + propertyBody: PropertyBody, + props: JayveeValidationProps, +) { + const lowerBoundProperty = propertyBody.properties.find( + (p) => p.name === 'lowerBound', + ); + const upperBoundProperty = propertyBody.properties.find( + (p) => p.name === 'upperBound', + ); + + if (lowerBoundProperty === undefined && upperBoundProperty === undefined) { + props.validationContext.accept( + 'hint', + 'This constraint should either specify an upper or lower bound, otherwise it has no effect.', + { node: propertyBody }, + ); + } + + if (lowerBoundProperty === undefined || upperBoundProperty === undefined) { + return; + } + + const lowerBound = evaluatePropertyValue( + lowerBoundProperty, + props.evaluationContext, + props.wrapperFactories, + props.valueTypeProvider.Primitives.Decimal, + ); + const upperBound = evaluatePropertyValue( + upperBoundProperty, + props.evaluationContext, + props.wrapperFactories, + props.valueTypeProvider.Primitives.Decimal, + ); + if (lowerBound === undefined || upperBound === undefined) { + return; + } + + if (lowerBound > upperBound) { + [lowerBoundProperty, upperBoundProperty].forEach((property) => { + props.validationContext.accept( + 'error', + 'The lower bound needs to be smaller or equal to the upper bound', + { node: property.value }, + ); + }); + return; + } + + const lowerBoundInclusiveProperty = propertyBody.properties.find( + (p) => p.name === 'lowerBoundInclusive', + ); + const upperBoundInclusiveProperty = propertyBody.properties.find( + (p) => p.name === 'upperBoundInclusive', + ); + + if (lowerBound === upperBound) { + let lowerBoundInclusive = true; + if (lowerBoundInclusiveProperty !== undefined) { + const expressionValue = evaluatePropertyValue( + lowerBoundInclusiveProperty, + props.evaluationContext, + props.wrapperFactories, + props.valueTypeProvider.Primitives.Boolean, + ); + if (expressionValue === undefined) { + return; + } + lowerBoundInclusive = expressionValue; + } + + let upperBoundInclusive = true; + if (upperBoundInclusiveProperty !== undefined) { + const expressionValue = evaluatePropertyValue( + upperBoundInclusiveProperty, + props.evaluationContext, + props.wrapperFactories, + props.valueTypeProvider.Primitives.Boolean, + ); + if (expressionValue === undefined) { + return; + } + upperBoundInclusive = expressionValue; + } + + const errorMessage = + 'Lower and upper bounds need to be inclusive if they are identical'; + if (!lowerBoundInclusive) { + props.validationContext.accept('error', errorMessage, { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + node: lowerBoundInclusiveProperty!.value, + }); + } + if (!upperBoundInclusive) { + props.validationContext.accept('error', errorMessage, { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + node: upperBoundInclusiveProperty!.value, + }); + } + } +} diff --git a/libs/language-server/src/lib/validation/checks/expression-constraint-definition.spec.ts b/libs/language-server/src/lib/validation/checks/expression-constraint-definition.spec.ts new file mode 100644 index 00000000..a05aba33 --- /dev/null +++ b/libs/language-server/src/lib/validation/checks/expression-constraint-definition.spec.ts @@ -0,0 +1,113 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type AstNode, + type AstNodeLocator, + type LangiumDocument, +} from 'langium'; +import { NodeFileSystem } from 'langium/node'; +import { vi } from 'vitest'; + +import { + type ExpressionConstraintDefinition, + type JayveeServices, + createJayveeServices, +} from '../../../lib'; +import { + type ParseHelperOptions, + createJayveeValidationProps, + expectNoParserAndLexerErrors, + parseHelper, + readJvTestAssetHelper, + validationAcceptorMockImpl, +} from '../../../test'; + +import { validateExpressionConstraintDefinition } from './expression-constraint-definition'; + +describe('Validation of ConstraintDefinition (expression syntax)', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + const validationAcceptorMock = vi.fn(validationAcceptorMockImpl); + + let locator: AstNodeLocator; + let services: JayveeServices; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../../test/assets/', + ); + + async function parseAndValidateExpressionConstraintDefinition(input: string) { + const document = await parse(input); + expectNoParserAndLexerErrors(document); + + const expressionConstraint = + locator.getAstNode<ExpressionConstraintDefinition>( + document.parseResult.value, + 'constraints@0', + ) as ExpressionConstraintDefinition; + + validateExpressionConstraintDefinition( + expressionConstraint, + createJayveeValidationProps(validationAcceptorMock, services), + ); + } + + beforeAll(() => { + // Create language services + services = createJayveeServices(NodeFileSystem).Jayvee; + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + afterEach(() => { + // Reset mock + validationAcceptorMock.mockReset(); + }); + + it('should have no error on valid expression constraint', async () => { + const text = readJvTestAsset( + 'expression-constraint-definition/valid-text-constraint.jv', + ); + + await parseAndValidateExpressionConstraintDefinition(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(0); + }); + + it('should diagnose error on incompatible type', async () => { + const text = readJvTestAsset( + 'expression-constraint-definition/invalid-incompatible-type.jv', + ); + + await parseAndValidateExpressionConstraintDefinition(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + `The value needs to be of type boolean but is of type integer`, + expect.any(Object), + ); + }); + + it('should diagnose info on simplifiable expression constraint', async () => { + const text = readJvTestAsset( + 'expression-constraint-definition/valid-simplify-info.jv', + ); + + await parseAndValidateExpressionConstraintDefinition(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'info', + `The expression can be simplified to 8`, + expect.any(Object), + ); + }); +}); diff --git a/libs/language-server/src/lib/validation/checks/expression-constraint-definition.ts b/libs/language-server/src/lib/validation/checks/expression-constraint-definition.ts new file mode 100644 index 00000000..6c7082ef --- /dev/null +++ b/libs/language-server/src/lib/validation/checks/expression-constraint-definition.ts @@ -0,0 +1,50 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** + * See https://jvalue.github.io/jayvee/docs/dev/guides/working-with-the-ast/ for why the following ESLint rule is disabled for this file. + */ +/* eslint-disable @typescript-eslint/no-unnecessary-condition */ + +import { inferExpressionType } from '../../ast/expressions/type-inference'; +import { type ExpressionConstraintDefinition } from '../../ast/generated/ast'; +import { type JayveeValidationProps } from '../validation-registry'; +import { checkExpressionSimplification } from '../validation-util'; + +export function validateExpressionConstraintDefinition( + constraint: ExpressionConstraintDefinition, + props: JayveeValidationProps, +): void { + checkConstraintExpression(constraint, props); +} + +function checkConstraintExpression( + constraint: ExpressionConstraintDefinition, + props: JayveeValidationProps, +): void { + const expression = constraint?.expression; + const inferredType = inferExpressionType( + expression, + props.validationContext, + props.valueTypeProvider, + props.wrapperFactories, + ); + if (inferredType === undefined) { + return; + } + + const expectedType = props.valueTypeProvider.Primitives.Boolean; + if (!inferredType.isConvertibleTo(expectedType)) { + props.validationContext.accept( + 'error', + `The value needs to be of type ${expectedType.getName()} but is of type ${inferredType.getName()}`, + { + node: expression, + }, + ); + return; + } + + checkExpressionSimplification(expression, props); +} diff --git a/libs/language-server/src/lib/validation/checks/jayvee-model.spec.ts b/libs/language-server/src/lib/validation/checks/jayvee-model.spec.ts new file mode 100644 index 00000000..51c04a2d --- /dev/null +++ b/libs/language-server/src/lib/validation/checks/jayvee-model.spec.ts @@ -0,0 +1,153 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type AstNode, type LangiumDocument } from 'langium'; +import { NodeFileSystem } from 'langium/node'; +import { vi } from 'vitest'; + +import { + type JayveeModel, + type JayveeServices, + createJayveeServices, +} from '../../../lib'; +import { + type ParseHelperOptions, + createJayveeValidationProps, + expectNoParserAndLexerErrors, + parseHelper, + readJvTestAssetHelper, + validationAcceptorMockImpl, +} from '../../../test'; + +import { validateJayveeModel } from './jayvee-model'; + +describe('Validation of JayveeModel', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + let services: JayveeServices; + + const validationAcceptorMock = vi.fn(validationAcceptorMockImpl); + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../../test/assets/', + ); + + async function parseAndValidateJayveeModel(input: string) { + const document = await parse(input); + expectNoParserAndLexerErrors(document); + + const jayveeModel = document.parseResult.value as JayveeModel; + + validateJayveeModel( + jayveeModel, + createJayveeValidationProps(validationAcceptorMock, services), + ); + } + + beforeAll(() => { + // Create language services + services = createJayveeServices(NodeFileSystem).Jayvee; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + afterEach(() => { + // Reset mock + validationAcceptorMock.mockReset(); + }); + + it('should diagnose error on non unique pipelines', async () => { + const text = readJvTestAsset( + 'jayvee-model/invalid-non-unique-pipelines.jv', + ); + + await parseAndValidateJayveeModel(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(2); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + `The pipelinedefinition name "Pipeline" needs to be unique.`, + expect.any(Object), + ); + }); + + it('should diagnose error on non unique transforms', async () => { + const text = readJvTestAsset( + 'jayvee-model/invalid-non-unique-transforms.jv', + ); + + await parseAndValidateJayveeModel(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(2); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + `The transformdefinition name "Transform" needs to be unique.`, + expect.any(Object), + ); + }); + + it('should diagnose error on non unique value types', async () => { + const text = readJvTestAsset( + 'jayvee-model/invalid-non-unique-value-types.jv', + ); + + await parseAndValidateJayveeModel(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(2); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + `The valuetypedefinition name "ValueType" needs to be unique.`, + expect.any(Object), + ); + }); + + it('should diagnose error on non unique value types (naming collision with builtin)', async () => { + const text = readJvTestAsset( + 'jayvee-model/invalid-duplicate-name-with-builtin-value-type.jv', + ); + + await parseAndValidateJayveeModel(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(2); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + `The valuetypedefinition name "DuplicateValuetype" needs to be unique.`, + expect.any(Object), + ); + }); + + it('should diagnose error on non unique constraints', async () => { + const text = readJvTestAsset( + 'jayvee-model/invalid-non-unique-constraints.jv', + ); + + await parseAndValidateJayveeModel(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(2); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + `The expressionconstraintdefinition name "Constraint" needs to be unique.`, + expect.any(Object), + ); + }); + + it('should diagnose error on non unique block types', async () => { + const text = readJvTestAsset( + 'jayvee-model/invalid-non-unique-block-types.jv', + ); + + await parseAndValidateJayveeModel(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(2); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + `The builtinblocktypedefinition name "TestBlock" needs to be unique.`, + expect.any(Object), + ); + }); +}); diff --git a/libs/language-server/src/lib/validation/checks/jayvee-model.ts b/libs/language-server/src/lib/validation/checks/jayvee-model.ts new file mode 100644 index 00000000..df136b07 --- /dev/null +++ b/libs/language-server/src/lib/validation/checks/jayvee-model.ts @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type JayveeModel } from '../../ast/generated/ast'; +import { type JayveeValidationProps } from '../validation-registry'; +import { checkUniqueNames } from '../validation-util'; + +export function validateJayveeModel( + model: JayveeModel, + props: JayveeValidationProps, +): void { + checkUniqueNames(model.pipelines, props.validationContext); + checkUniqueNames(model.transforms, props.validationContext); + checkUniqueNames(model.valueTypes, props.validationContext); + checkUniqueNames(model.constraints, props.validationContext); + checkUniqueNames(model.blockTypes, props.validationContext); + checkUniqueNames(model.constrainttypes, props.validationContext); + checkUniqueNames(model.iotypes, props.validationContext); +} diff --git a/libs/language-server/src/lib/validation/checks/pipe-definition.spec.ts b/libs/language-server/src/lib/validation/checks/pipe-definition.spec.ts new file mode 100644 index 00000000..d884298b --- /dev/null +++ b/libs/language-server/src/lib/validation/checks/pipe-definition.spec.ts @@ -0,0 +1,131 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type AstNode, + type AstNodeLocator, + type LangiumDocument, +} from 'langium'; +import { NodeFileSystem } from 'langium/node'; +import { vi } from 'vitest'; + +import { + type JayveeServices, + type PipeDefinition, + createJayveeServices, +} from '../../../lib'; +import { + type ParseHelperOptions, + createJayveeValidationProps, + expectNoParserAndLexerErrors, + parseHelper, + readJvTestAssetHelper, + validationAcceptorMockImpl, +} from '../../../test'; + +import { validatePipeDefinition } from './pipe-definition'; + +describe('Validation of PipeDefinition', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + const validationAcceptorMock = vi.fn(validationAcceptorMockImpl); + + let locator: AstNodeLocator; + let services: JayveeServices; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../../test/assets/', + ); + + async function parseAndValidatePipe(input: string) { + const document = await parse(input); + expectNoParserAndLexerErrors(document); + + const pipe = locator.getAstNode<PipeDefinition>( + document.parseResult.value, + 'pipelines@0/pipes@0', + ) as PipeDefinition; + + validatePipeDefinition( + pipe, + createJayveeValidationProps(validationAcceptorMock, services), + ); + } + + beforeAll(() => { + // Create language services + services = createJayveeServices(NodeFileSystem).Jayvee; + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + afterEach(() => { + // Reset mock + validationAcceptorMock.mockReset(); + }); + + describe('PipeDefinition syntax', () => { + // This test should succeed, because the error is thrown by langium during linking, not during validation! + it('should have no error even if pipe references non existing block', async () => { + const text = readJvTestAsset('pipe-definition/valid-undefined-block.jv'); + + await parseAndValidatePipe(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(0); + }); + + it('should have no error even if pipe references block of non existing type', async () => { + const text = readJvTestAsset( + 'pipe-definition/valid-unknown-block-type.jv', + ); + + await parseAndValidatePipe(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(0); + }); + + it('should diagnose error on unsupported pipe between BlockTypes', async () => { + const text = readJvTestAsset( + 'pipe-definition/invalid-pipe-between-block-types.jv', + ); + + await parseAndValidatePipe(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(2); + expect(validationAcceptorMock).toHaveBeenNthCalledWith( + 2, + 'error', + 'The output type "File" of block "TestExtractor" (of type "TestFileExtractor") is not compatible with the input type "Table" of block "TestLoader" (of type "TestTableLoader")', + expect.any(Object), + ); + }); + + it('should diagnose error on connecting loader block to extractor block with a pipe', async () => { + const text = readJvTestAsset( + 'pipe-definition/invalid-output-block-as-input.jv', + ); + + await parseAndValidatePipe(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(2); + expect(validationAcceptorMock).toHaveBeenNthCalledWith( + 1, + 'error', + 'Block "BlockTo" cannot be connected to other blocks. Its block type "TestFileLoader" has output type "None".', + expect.any(Object), + ); + expect(validationAcceptorMock).toHaveBeenNthCalledWith( + 2, + 'error', + 'Block "BlockFrom" cannot be connected to from other blocks. Its block type "TestFileExtractor" has input type "None".', + expect.any(Object), + ); + }); + }); +}); diff --git a/libs/language-server/src/lib/validation/checks/pipe-definition.ts b/libs/language-server/src/lib/validation/checks/pipe-definition.ts new file mode 100644 index 00000000..b22fc765 --- /dev/null +++ b/libs/language-server/src/lib/validation/checks/pipe-definition.ts @@ -0,0 +1,77 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** + * See https://jvalue.github.io/jayvee/docs/dev/guides/working-with-the-ast/ for why the following ESLint rule is disabled for this file. + */ +/* eslint-disable @typescript-eslint/no-unnecessary-condition */ + +import { type PipeDefinition } from '../../ast/generated/ast'; +import { type JayveeValidationProps } from '../validation-registry'; + +export function validatePipeDefinition( + pipe: PipeDefinition, + props: JayveeValidationProps, +): void { + checkBlockCompatibility(pipe, props); +} + +function checkBlockCompatibility( + pipe: PipeDefinition, + props: JayveeValidationProps, +): void { + const pipeWrappers = props.wrapperFactories.Pipe.wrapAll(pipe); + for (const pipeWrapper of pipeWrappers) { + const fromBlockTypeDefinition = pipeWrapper.from?.type; + const toBlockTypeDefinition = pipeWrapper.to?.type; + + if ( + !props.wrapperFactories.BlockType.canWrap(fromBlockTypeDefinition) || + !props.wrapperFactories.BlockType.canWrap(toBlockTypeDefinition) + ) { + continue; + } + const fromBlockType = props.wrapperFactories.BlockType.wrap( + fromBlockTypeDefinition, + ); + const toBlockType = props.wrapperFactories.BlockType.wrap( + toBlockTypeDefinition, + ); + + const isFromBlockLoader = !fromBlockType.hasOutput(); + const isToBlockExtractor = !toBlockType.hasInput(); + + if (isFromBlockLoader) { + const errorMessage = `Block "${pipeWrapper.from?.name}" cannot be connected to other blocks. Its block type "${fromBlockType.astNode.name}" has output type "${fromBlockType.outputType}".`; + props.validationContext.accept( + 'error', + errorMessage, + pipeWrapper.getFromDiagnostic(), + ); + } + + if (isToBlockExtractor) { + const errorMessage = `Block "${pipeWrapper.to?.name}" cannot be connected to from other blocks. Its block type "${toBlockType.astNode.name}" has input type "${toBlockType.inputType}".`; + props.validationContext.accept( + 'error', + errorMessage, + pipeWrapper.getToDiagnostic(), + ); + } + + if (!fromBlockType.canBeConnectedTo(toBlockType)) { + const errorMessage = `The output type "${fromBlockType.outputType}" of block "${pipeWrapper.from?.name}" (of type "${fromBlockType.astNode.name}") is not compatible with the input type "${toBlockType.inputType}" of block "${pipeWrapper.to?.name}" (of type "${toBlockType.astNode.name}")`; + props.validationContext.accept( + 'error', + errorMessage, + pipeWrapper.getFromDiagnostic(), + ); + props.validationContext.accept( + 'error', + errorMessage, + pipeWrapper.getToDiagnostic(), + ); + } + } +} diff --git a/libs/language-server/src/lib/validation/checks/pipeline-definition.spec.ts b/libs/language-server/src/lib/validation/checks/pipeline-definition.spec.ts new file mode 100644 index 00000000..71cd9471 --- /dev/null +++ b/libs/language-server/src/lib/validation/checks/pipeline-definition.spec.ts @@ -0,0 +1,139 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type AstNode, + type AstNodeLocator, + type LangiumDocument, +} from 'langium'; +import { NodeFileSystem } from 'langium/node'; +import { vi } from 'vitest'; + +import { + type JayveeServices, + type PipelineDefinition, + createJayveeServices, +} from '../../../lib'; +import { + type ParseHelperOptions, + createJayveeValidationProps, + expectNoParserAndLexerErrors, + parseHelper, + readJvTestAssetHelper, + validationAcceptorMockImpl, +} from '../../../test'; + +import { validatePipelineDefinition } from './pipeline-definition'; + +describe('Validation of PipelineDefinition', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + const validationAcceptorMock = vi.fn(validationAcceptorMockImpl); + + let locator: AstNodeLocator; + let services: JayveeServices; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../../test/assets/', + ); + + async function parseAndValidatePipeline(input: string) { + const document = await parse(input); + expectNoParserAndLexerErrors(document); + + const pipeline = locator.getAstNode<PipelineDefinition>( + document.parseResult.value, + 'pipelines@0', + ) as PipelineDefinition; + + validatePipelineDefinition( + pipeline, + createJayveeValidationProps(validationAcceptorMock, services), + ); + } + + beforeAll(() => { + // Create language services + services = createJayveeServices(NodeFileSystem).Jayvee; + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + afterEach(() => { + // Reset mock + validationAcceptorMock.mockReset(); + }); + + it('should diagnose error on missing starting block (no blocks)', async () => { + const text = readJvTestAsset( + 'pipeline-definition/invalid-empty-pipeline.jv', + ); + + await parseAndValidatePipeline(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + `An extractor block is required for this pipeline`, + expect.any(Object), + ); + }); + + it('should diagnose error on missing starting block (no pipes)', async () => { + const text = readJvTestAsset( + 'pipeline-definition/invalid-pipeline-only-blocks.jv', + ); + + await parseAndValidatePipeline(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(2); // one warning for unused blocks + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + `An extractor block is required for this pipeline`, + expect.any(Object), + ); + }); + + it('should have no error on valid pipeline', async () => { + const text = readJvTestAsset('pipeline-definition/valid-pipeline.jv'); + + await parseAndValidatePipeline(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(0); + }); + + it('should diagnose error on block as input for multiple pipes', async () => { + const text = readJvTestAsset( + 'pipeline-definition/invalid-block-as-multiple-pipe-inputs.jv', + ); + + await parseAndValidatePipeline(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(2); + expect(validationAcceptorMock).toHaveBeenNthCalledWith( + 2, + 'error', + 'At most one pipe can be connected to the input of a block. Currently, the following 2 blocks are connected via pipes: "BlockFrom1", "BlockFrom2"', + expect.any(Object), + ); + }); + + it('should diagnose error on block without pipe', async () => { + const text = readJvTestAsset('pipeline-definition/invalid-missing-pipe.jv'); + + await parseAndValidatePipeline(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(2); // one error since missing extractor + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'warning', + 'A pipe should be connected to the output of this block', + expect.any(Object), + ); + }); +}); diff --git a/libs/language-server/src/lib/validation/checks/pipeline-definition.ts b/libs/language-server/src/lib/validation/checks/pipeline-definition.ts new file mode 100644 index 00000000..d5333ad8 --- /dev/null +++ b/libs/language-server/src/lib/validation/checks/pipeline-definition.ts @@ -0,0 +1,184 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type PipeWrapper, type PipelineWrapper } from '../../ast'; +import { + type BlockDefinition, + type CompositeBlockTypeDefinition, + type PipelineDefinition, +} from '../../ast/generated/ast'; +import { type JayveeValidationProps } from '../validation-registry'; +import { checkUniqueNames } from '../validation-util'; + +export function validatePipelineDefinition( + pipeline: PipelineDefinition, + props: JayveeValidationProps, +): void { + checkStartingBlocks(pipeline, props); + checkUniqueNames(pipeline.blocks, props.validationContext); + checkUniqueNames(pipeline.transforms, props.validationContext); + checkUniqueNames(pipeline.valueTypes, props.validationContext); + checkUniqueNames(pipeline.constraints, props.validationContext); + + checkMultipleBlockInputs(pipeline, props); + checkDefinedBlocksAreUsed(pipeline, props); +} + +function checkStartingBlocks( + pipeline: PipelineDefinition, + props: JayveeValidationProps, +): void { + if (!props.wrapperFactories.Pipeline.canWrap(pipeline)) { + return; + } + const pipelineWrapper = props.wrapperFactories.Pipeline.wrap(pipeline); + + const startingBlocks = pipelineWrapper.getStartingBlocks(); + if (startingBlocks.length === 0) { + props.validationContext.accept( + 'error', + `An extractor block is required for this pipeline`, + { + node: pipeline, + property: 'name', + }, + ); + } +} + +export function checkMultipleBlockInputs( + pipeline: PipelineDefinition | CompositeBlockTypeDefinition, + props: JayveeValidationProps, +): void { + if (!props.wrapperFactories.Pipeline.canWrap(pipeline)) { + return; + } + const pipelineWrapper = props.wrapperFactories.Pipeline.wrap(pipeline); + + const startingBlocks = pipelineWrapper.getStartingBlocks(); + let alreadyMarkedPipes: PipeWrapper[] = []; + for (const startingBlock of startingBlocks) { + alreadyMarkedPipes = doCheckMultipleBlockInputs( + pipelineWrapper, + startingBlock, + alreadyMarkedPipes, + props, + ); + } +} + +/** + * Inner method to check recursively whether blocks in a pipeline have multiple inputs + * @param pipelineWrapper The wrapping pipeline + * @param block The current block + * @param alreadyMarkedPipes List of already visited pipes to avoid duplicate errors + * @param context The validation context + * @returns the updated @alreadyMarkedPipes with all marked pipes + */ +function doCheckMultipleBlockInputs( + pipelineWrapper: PipelineWrapper< + PipelineDefinition | CompositeBlockTypeDefinition + >, + block: BlockDefinition, + alreadyMarkedPipes: PipeWrapper[], + props: JayveeValidationProps, +): PipeWrapper[] { + const pipesFromParents = pipelineWrapper.getIngoingPipes(block); + if (pipesFromParents.length > 1) { + const parentBlockNames = pipesFromParents.map( + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + (pipe) => '"' + pipe.from?.name + '"', + ); + for (const pipe of pipesFromParents) { + const wasAlreadyMarked = alreadyMarkedPipes.some((x) => pipe.equals(x)); + if (wasAlreadyMarked) { + continue; + } + + props.validationContext.accept( + 'error', + `At most one pipe can be connected to the input of a block. Currently, the following ${ + pipesFromParents.length + } blocks are connected via pipes: ${parentBlockNames.join(', ')}`, + pipe.getToDiagnostic(), + ); + + alreadyMarkedPipes.push(pipe); + } + } + + const children = pipelineWrapper.getChildBlocks(block); + for (const child of children) { + alreadyMarkedPipes = doCheckMultipleBlockInputs( + pipelineWrapper, + child, + alreadyMarkedPipes, + props, + ); + } + + return alreadyMarkedPipes; +} + +export function checkDefinedBlocksAreUsed( + pipeline: PipelineDefinition | CompositeBlockTypeDefinition, + props: JayveeValidationProps, +): void { + if (!props.wrapperFactories.Pipeline.canWrap(pipeline)) { + return; + } + const pipelineWrapper = props.wrapperFactories.Pipeline.wrap(pipeline); + + const containedBlocks = pipeline.blocks; + for (const block of containedBlocks) { + doCheckDefinedBlockIsUsed(pipelineWrapper, block, props); + } +} + +function doCheckDefinedBlockIsUsed( + pipelineWrapper: PipelineWrapper< + PipelineDefinition | CompositeBlockTypeDefinition + >, + block: BlockDefinition, + props: JayveeValidationProps, +): void { + if ( + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + block.type === undefined || + !props.wrapperFactories.BlockType.canWrap(block.type) + ) { + return; + } + const blockType = props.wrapperFactories.BlockType.wrap(block.type); + + const isExtractorBlock = !blockType.hasInput(); + if (!isExtractorBlock) { + const parents = pipelineWrapper.getParentBlocks(block); + if (parents.length === 0) { + props.validationContext.accept( + 'warning', + `A pipe should be connected to the input of this block`, + { + node: block, + property: 'name', + }, + ); + } + } + + const isLoaderBlock = !blockType.hasOutput(); + if (!isLoaderBlock) { + const children = pipelineWrapper.getChildBlocks(block); + if (children.length === 0) { + props.validationContext.accept( + 'warning', + `A pipe should be connected to the output of this block`, + { + node: block, + property: 'name', + }, + ); + } + } +} diff --git a/libs/language-server/src/lib/validation/checks/property-assignment.spec.ts b/libs/language-server/src/lib/validation/checks/property-assignment.spec.ts new file mode 100644 index 00000000..08b3e664 --- /dev/null +++ b/libs/language-server/src/lib/validation/checks/property-assignment.spec.ts @@ -0,0 +1,181 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type AstNode, + type AstNodeLocator, + type LangiumDocument, +} from 'langium'; +import { NodeFileSystem } from 'langium/node'; +import { vi } from 'vitest'; + +import { + type JayveeServices, + type PropertyAssignment, + type PropertyBody, + type TypedObjectWrapper, + createJayveeServices, +} from '../../../lib'; +import { + type ParseHelperOptions, + createJayveeValidationProps, + expectNoParserAndLexerErrors, + parseHelper, + readJvTestAssetHelper, + validationAcceptorMockImpl, +} from '../../../test'; + +import { validatePropertyAssignment } from './property-assignment'; + +describe('Validation of PropertyAssignment', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + const validationAcceptorMock = vi.fn(validationAcceptorMockImpl); + + let locator: AstNodeLocator; + let services: JayveeServices; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../../test/assets/', + ); + + async function parseAndValidatePropertyAssignment(input: string) { + const document = await parse(input); + expectNoParserAndLexerErrors(document); + + const propertyBody = locator.getAstNode<PropertyBody>( + document.parseResult.value, + 'pipelines@0/blocks@0/body', + ) as PropertyBody; + + const type = propertyBody.$container.type; + + const props = createJayveeValidationProps(validationAcceptorMock, services); + const wrapper = props.wrapperFactories.TypedObject.wrap(type); + expect(wrapper).toBeDefined(); + + const propertyAssignment = locator.getAstNode<PropertyAssignment>( + propertyBody, + 'properties@0', + ) as PropertyAssignment; + + validatePropertyAssignment( + propertyAssignment, + wrapper as TypedObjectWrapper, + props, + ); + } + + beforeAll(() => { + // Create language services + services = createJayveeServices(NodeFileSystem).Jayvee; + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + afterEach(() => { + // Reset mock + validationAcceptorMock.mockReset(); + }); + + it('should diagnose error on invalid property name', async () => { + const text = readJvTestAsset( + 'property-assignment/invalid-unknown-property.jv', + ); + + await parseAndValidatePropertyAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + `Invalid property name "unknownProperty".`, + expect.any(Object), + ); + }); + + describe('Validation of RuntimeParameterLiteral assignment', () => { + it('should have no error on runtime parameter for text property', async () => { + const text = readJvTestAsset( + 'property-assignment/valid-runtime-property.jv', + ); + + await parseAndValidatePropertyAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(0); + }); + + it('should diagnose error on runtime parameter for regex property', async () => { + const text = readJvTestAsset( + 'property-assignment/invalid-runtime-property.jv', + ); + + await parseAndValidatePropertyAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + `Runtime parameters are not allowed for properties of type Regex`, + expect.any(Object), + ); + }); + }); + + it('should diagnose error on invalid property typing', async () => { + const text = readJvTestAsset( + 'property-assignment/invalid-property-type.jv', + ); + + await parseAndValidatePropertyAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + `The value of property "textProperty" needs to be of type text but is of type integer`, + expect.any(Object), + ); + }); + + it('should diagnose info on simplifiable property expression', async () => { + const text = readJvTestAsset('property-assignment/valid-simplify-info.jv'); + + await parseAndValidatePropertyAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'info', + `The expression can be simplified to 1019`, + expect.any(Object), + ); + }); + + it('should diagnose info on simplifiable property sub-expression', async () => { + const text = readJvTestAsset( + 'property-assignment/valid-simplify-info-sub-expression.jv', + ); + + await parseAndValidatePropertyAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'info', + `The expression can be simplified to 30`, + expect.any(Object), + ); + }); + + it('should diagnose info on non simplifiable property expression', async () => { + const text = readJvTestAsset( + 'property-assignment/valid-uneccessarysimplify-info.jv', + ); + + await parseAndValidatePropertyAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(0); + }); +}); diff --git a/libs/language-server/src/lib/validation/checks/property-assignment.ts b/libs/language-server/src/lib/validation/checks/property-assignment.ts new file mode 100644 index 00000000..e53e1f7a --- /dev/null +++ b/libs/language-server/src/lib/validation/checks/property-assignment.ts @@ -0,0 +1,115 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** + * See https://jvalue.github.io/jayvee/docs/dev/guides/working-with-the-ast/ for why the following ESLint rule is disabled for this file. + */ +/* eslint-disable @typescript-eslint/no-unnecessary-condition */ + +import { + type PropertySpecification, + type TypedObjectWrapper, + inferExpressionType, +} from '../../ast'; +import { + type PropertyAssignment, + isBlockTypeProperty, + isRuntimeParameterLiteral, +} from '../../ast/generated/ast'; +import { type JayveeValidationProps } from '../validation-registry'; +import { checkExpressionSimplification } from '../validation-util'; + +import { checkBlockTypeSpecificProperties } from './block-type-specific/property-assignment'; + +export function validatePropertyAssignment( + property: PropertyAssignment, + wrapper: TypedObjectWrapper, + props: JayveeValidationProps, +): void { + const propertySpec = wrapper.getPropertySpecification(property?.name); + + checkPropertyNameValidity(property, propertySpec, props); + + if (propertySpec === undefined) { + return; + } + checkPropertyValueTyping(property, propertySpec, props); + + if (props.validationContext.hasErrorOccurred()) { + return; + } + + checkBlockTypeSpecificProperties(property, propertySpec, props); +} + +function checkPropertyNameValidity( + property: PropertyAssignment, + propertySpec: PropertySpecification | undefined, + props: JayveeValidationProps, +): void { + if (propertySpec === undefined) { + props.validationContext.accept( + 'error', + `Invalid property name "${property?.name ?? ''}".`, + { + node: property, + property: 'name', + }, + ); + } +} + +function checkPropertyValueTyping( + property: PropertyAssignment, + propertySpec: PropertySpecification, + props: JayveeValidationProps, +): void { + const propertyType = propertySpec.type; + const propertyValue = property?.value; + if (propertyValue === undefined) { + return; + } + + if (isRuntimeParameterLiteral(propertyValue)) { + if (!propertyType.isAllowedAsRuntimeParameter()) { + props.validationContext.accept( + 'error', + `Runtime parameters are not allowed for properties of type ${propertyType.getName()}`, + { + node: propertyValue, + }, + ); + } + return; + } + + if (isBlockTypeProperty(propertyValue)) { + return; + } + + const inferredType = inferExpressionType( + propertyValue, + props.validationContext, + props.valueTypeProvider, + props.wrapperFactories, + ); + if (inferredType === undefined) { + return; + } + + if (!inferredType.isConvertibleTo(propertyType)) { + props.validationContext.accept( + 'error', + `The value of property "${ + property.name + }" needs to be of type ${propertyType.getName()} but is of type ${inferredType.getName()}`, + { + node: propertyValue, + }, + ); + return; + } + + checkExpressionSimplification(propertyValue, props); +} diff --git a/libs/language-server/src/lib/validation/checks/property-body.spec.ts b/libs/language-server/src/lib/validation/checks/property-body.spec.ts new file mode 100644 index 00000000..215d416b --- /dev/null +++ b/libs/language-server/src/lib/validation/checks/property-body.spec.ts @@ -0,0 +1,108 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type AstNode, + type AstNodeLocator, + type LangiumDocument, +} from 'langium'; +import { NodeFileSystem } from 'langium/node'; +import { vi } from 'vitest'; + +import { + type JayveeServices, + type PropertyBody, + createJayveeServices, +} from '../../../lib'; +import { + type ParseHelperOptions, + createJayveeValidationProps, + expectNoParserAndLexerErrors, + parseHelper, + readJvTestAssetHelper, + validationAcceptorMockImpl, +} from '../../../test'; + +import { validatePropertyBody } from './property-body'; + +describe('Validation PropertyBody', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + const validationAcceptorMock = vi.fn(validationAcceptorMockImpl); + + let locator: AstNodeLocator; + let services: JayveeServices; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../../test/assets/', + ); + + async function parseAndValidatePropertyBody(input: string) { + const document = await parse(input); + expectNoParserAndLexerErrors(document); + + const propertyBody = locator.getAstNode<PropertyBody>( + document.parseResult.value, + 'pipelines@0/blocks@0/body', + ) as PropertyBody; + + validatePropertyBody( + propertyBody, + createJayveeValidationProps(validationAcceptorMock, services), + ); + } + + beforeAll(() => { + // Create language services + services = createJayveeServices(NodeFileSystem).Jayvee; + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + afterEach(() => { + // Reset mock + validationAcceptorMock.mockReset(); + }); + + it('should diagnose error on missing properties', async () => { + const text = readJvTestAsset('property-body/invalid-missing-property.jv'); + + await parseAndValidatePropertyBody(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + `The following required properties are missing: "textProperty"`, + expect.any(Object), + ); + }); + + it('should have no error on missing properties with default values', async () => { + const text = readJvTestAsset('property-body/valid-default-values.jv'); + + await parseAndValidatePropertyBody(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(0); + }); + + it('should diagnose error on failed property validation', async () => { + const text = readJvTestAsset( + 'property-body/invalid-property-validation-failed.jv', + ); + + await parseAndValidatePropertyBody(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + 'The value of property "customValidationTextProperty" needs to be of type CustomValuetype but is of type text', + expect.any(Object), + ); + }); +}); diff --git a/libs/language-server/src/lib/validation/checks/property-body.ts b/libs/language-server/src/lib/validation/checks/property-body.ts new file mode 100644 index 00000000..51379011 --- /dev/null +++ b/libs/language-server/src/lib/validation/checks/property-body.ts @@ -0,0 +1,95 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** + * See https://jvalue.github.io/jayvee/docs/dev/guides/working-with-the-ast/ for why the following ESLint rule is disabled for this file. + */ +/* eslint-disable @typescript-eslint/no-unnecessary-condition */ + +import { type TypedObjectWrapper } from '../../ast'; +import { + type PropertyAssignment, + type PropertyBody, + isBlockDefinition, + isTypedConstraintDefinition, +} from '../../ast/generated/ast'; +import { type JayveeValidationProps } from '../validation-registry'; +import { checkUniqueNames } from '../validation-util'; + +import { checkBlockTypeSpecificPropertyBody } from './block-type-specific/property-body'; +import { checkConstraintTypeSpecificPropertyBody } from './constrainttype-specific/property-body'; +import { validatePropertyAssignment } from './property-assignment'; + +export function validatePropertyBody( + propertyBody: PropertyBody, + props: JayveeValidationProps, +): void { + const properties = propertyBody?.properties ?? []; + checkUniqueNames(properties, props.validationContext); + + const wrapper = inferTypedObjectWrapper(propertyBody, props); + if (wrapper === undefined) { + return; + } + + checkPropertyCompleteness(propertyBody, properties, wrapper, props); + for (const property of propertyBody.properties) { + validatePropertyAssignment(property, wrapper, props); + } + if (props.validationContext.hasErrorOccurred()) { + return; + } + + checkCustomPropertyValidation(propertyBody, wrapper, props); +} + +function inferTypedObjectWrapper( + propertyBody: PropertyBody, + props: JayveeValidationProps, +): TypedObjectWrapper | undefined { + const type = propertyBody.$container?.type.ref; + return props.wrapperFactories.TypedObject.wrap(type); +} + +function checkPropertyCompleteness( + propertyBody: PropertyBody, + properties: PropertyAssignment[], + wrapper: TypedObjectWrapper, + props: JayveeValidationProps, +): void { + const presentPropertyNames = properties.map((property) => property.name); + const missingRequiredPropertyNames = + wrapper.getMissingRequiredPropertyNames(presentPropertyNames); + + if (missingRequiredPropertyNames.length > 0) { + props.validationContext.accept( + 'error', + `The following required properties are missing: ${missingRequiredPropertyNames + .map((name) => `"${name}"`) + .join(', ')}`, + { + node: propertyBody.$container, + property: 'type', + }, + ); + } +} + +function checkCustomPropertyValidation( + propertyBody: PropertyBody, + wrapper: TypedObjectWrapper, + props: JayveeValidationProps, +): void { + wrapper.validate( + propertyBody, + props.validationContext, + props.evaluationContext, + ); + + if (isBlockDefinition(propertyBody.$container)) { + checkBlockTypeSpecificPropertyBody(propertyBody, props); + } else if (isTypedConstraintDefinition(propertyBody.$container)) { + checkConstraintTypeSpecificPropertyBody(propertyBody, props); + } +} diff --git a/libs/language-server/src/lib/validation/checks/range-literal.spec.ts b/libs/language-server/src/lib/validation/checks/range-literal.spec.ts new file mode 100644 index 00000000..175a5f21 --- /dev/null +++ b/libs/language-server/src/lib/validation/checks/range-literal.spec.ts @@ -0,0 +1,105 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type AstNode, + type AstNodeLocator, + type LangiumDocument, +} from 'langium'; +import { NodeFileSystem } from 'langium/node'; +import { vi } from 'vitest'; + +import { + type JayveeServices, + type RangeLiteral, + createJayveeServices, +} from '../../../lib'; +import { + type ParseHelperOptions, + createJayveeValidationProps, + expectNoParserAndLexerErrors, + parseHelper, + readJvTestAssetHelper, + validationAcceptorMockImpl, +} from '../../../test'; + +import { validateRangeLiteral } from './range-literal'; + +describe('Validation of RangeLiteral', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + const validationAcceptorMock = vi.fn(validationAcceptorMockImpl); + + let locator: AstNodeLocator; + let services: JayveeServices; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../../test/assets/', + ); + + async function parseAndValidateRangeLiteral(input: string) { + const document = await parse(input); + expectNoParserAndLexerErrors(document); + + const rangeLiteral = locator.getAstNode<RangeLiteral>( + document.parseResult.value, + 'pipelines@0/blocks@0/body/properties@0/value', + ) as RangeLiteral; + + validateRangeLiteral( + rangeLiteral, + createJayveeValidationProps(validationAcceptorMock, services), + ); + } + + beforeAll(() => { + // Create language services + services = createJayveeServices(NodeFileSystem).Jayvee; + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + afterEach(() => { + // Reset mock + validationAcceptorMock.mockReset(); + }); + + it('should have no error on valid range literal', async () => { + const text = readJvTestAsset('range-literal/valid-range-literal.jv'); + + await parseAndValidateRangeLiteral(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(0); + }); + + it('should diagnose error on wrong range direction', async () => { + const text = readJvTestAsset( + 'range-literal/invalid-range-literal-start-after-end.jv', + ); + + await parseAndValidateRangeLiteral(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + `Cell ranges need to be spanned from top-left to bottom-right`, + expect.any(Object), + ); + }); + + it('should have no error on unlimited range', async () => { + const text = readJvTestAsset( + 'range-literal/valid-range-literal-unlimited-range.jv', + ); + + await parseAndValidateRangeLiteral(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(0); + }); +}); diff --git a/libs/language-server/src/lib/validation/checks/range-literal.ts b/libs/language-server/src/lib/validation/checks/range-literal.ts new file mode 100644 index 00000000..dbf994bb --- /dev/null +++ b/libs/language-server/src/lib/validation/checks/range-literal.ts @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type RangeLiteral } from '../../ast/generated/ast'; +import { CellRangeWrapper } from '../../ast/wrappers/cell-range-wrapper'; +import { type JayveeValidationProps } from '../validation-registry'; + +export function validateRangeLiteral( + range: RangeLiteral, + props: JayveeValidationProps, +): void { + if (!CellRangeWrapper.canBeWrapped(range)) { + return; + } + const wrappedRange = new CellRangeWrapper(range); + checkRangeLimits(wrappedRange, props); +} + +function checkRangeLimits( + range: CellRangeWrapper, + props: JayveeValidationProps, +): void { + if ( + range.from.columnIndex > range.to.columnIndex || + range.from.rowIndex > range.to.rowIndex + ) { + props.validationContext.accept( + 'error', + `Cell ranges need to be spanned from top-left to bottom-right`, + { + node: range.astNode, + }, + ); + } +} diff --git a/libs/language-server/src/lib/validation/checks/regex-literal.spec.ts b/libs/language-server/src/lib/validation/checks/regex-literal.spec.ts new file mode 100644 index 00000000..81561f70 --- /dev/null +++ b/libs/language-server/src/lib/validation/checks/regex-literal.spec.ts @@ -0,0 +1,95 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type AstNode, + type AstNodeLocator, + type LangiumDocument, +} from 'langium'; +import { NodeFileSystem } from 'langium/node'; +import { vi } from 'vitest'; + +import { + type JayveeServices, + type RegexLiteral, + createJayveeServices, +} from '../../../lib'; +import { + type ParseHelperOptions, + createJayveeValidationProps, + expectNoParserAndLexerErrors, + parseHelper, + readJvTestAssetHelper, + validationAcceptorMockImpl, +} from '../../../test'; + +import { validateRegexLiteral } from './regex-literal'; + +describe('Validation of RegexLiteral', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + const validationAcceptorMock = vi.fn(validationAcceptorMockImpl); + + let locator: AstNodeLocator; + let services: JayveeServices; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../../test/assets/', + ); + + async function parseAndValidateRangeLiteral(input: string) { + const document = await parse(input); + expectNoParserAndLexerErrors(document); + + const regexLiteral = locator.getAstNode<RegexLiteral>( + document.parseResult.value, + 'pipelines@0/blocks@0/body/properties@0/value', + ) as RegexLiteral; + + validateRegexLiteral( + regexLiteral, + createJayveeValidationProps(validationAcceptorMock, services), + ); + } + + beforeAll(() => { + // Create language services + services = createJayveeServices(NodeFileSystem).Jayvee; + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + afterEach(() => { + // Reset mock + validationAcceptorMock.mockReset(); + }); + + it('should have no error on valid regex literal', async () => { + const text = readJvTestAsset('regex-literal/valid-regex-literal.jv'); + + await parseAndValidateRangeLiteral(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(0); + }); + + it('should diagnose error on regex syntax error', async () => { + const text = readJvTestAsset( + 'regex-literal/invalid-regex-literal-syntax-error.jv', + ); + + await parseAndValidateRangeLiteral(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + `A parsing error occurred: Invalid regular expression: /a[+/: Unterminated character class`, + expect.any(Object), + ); + }); +}); diff --git a/libs/language-server/src/lib/validation/checks/regex-literal.ts b/libs/language-server/src/lib/validation/checks/regex-literal.ts new file mode 100644 index 00000000..e44f4de1 --- /dev/null +++ b/libs/language-server/src/lib/validation/checks/regex-literal.ts @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type RegexLiteral } from '../../ast/generated/ast'; +import { type JayveeValidationProps } from '../validation-registry'; + +/** + * See https://jvalue.github.io/jayvee/docs/dev/guides/working-with-the-ast/ for why the following ESLint rule is disabled for this file. + */ +/* eslint-disable @typescript-eslint/no-unnecessary-condition */ + +export function validateRegexLiteral( + regex: RegexLiteral, + props: JayveeValidationProps, +): void { + checkRegexParsability(regex, props); +} + +function checkRegexParsability( + regex: RegexLiteral, + props: JayveeValidationProps, +): void { + const regexValue = regex?.value; + if (regexValue === undefined) { + return; + } + try { + new RegExp(regexValue); + } catch (error) { + if (error instanceof SyntaxError) { + props.validationContext.accept( + 'error', + `A parsing error occurred: ${error.message}`, + { + node: regex, + }, + ); + } else { + props.validationContext.accept( + 'error', + `An unknown parsing error occurred: ${JSON.stringify(error)}.`, + { + node: regex, + }, + ); + } + } +} diff --git a/libs/language-server/src/lib/validation/checks/transform-body.spec.ts b/libs/language-server/src/lib/validation/checks/transform-body.spec.ts new file mode 100644 index 00000000..e1ce55e9 --- /dev/null +++ b/libs/language-server/src/lib/validation/checks/transform-body.spec.ts @@ -0,0 +1,181 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type AstNode, + type AstNodeLocator, + type LangiumDocument, +} from 'langium'; +import { NodeFileSystem } from 'langium/node'; +import { vi } from 'vitest'; + +import { + type JayveeServices, + type TransformBody, + createJayveeServices, +} from '../../../lib'; +import { + type ParseHelperOptions, + createJayveeValidationProps, + expectNoParserAndLexerErrors, + parseHelper, + readJvTestAssetHelper, + validationAcceptorMockImpl, +} from '../../../test'; + +import { validateTransformBody } from './transform-body'; + +describe('Validation of TransformBody', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + const validationAcceptorMock = vi.fn(validationAcceptorMockImpl); + + let locator: AstNodeLocator; + let services: JayveeServices; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../../test/assets/', + ); + + async function parseAndValidateTransformBody(input: string) { + const document = await parse(input); + expectNoParserAndLexerErrors(document); + + const transformBody = locator.getAstNode<TransformBody>( + document.parseResult.value, + 'transforms@0/body', + ) as TransformBody; + + validateTransformBody( + transformBody, + createJayveeValidationProps(validationAcceptorMock, services), + ); + } + + beforeAll(() => { + // Create language services + services = createJayveeServices(NodeFileSystem).Jayvee; + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + afterEach(() => { + // Reset mock + validationAcceptorMock.mockReset(); + }); + + it('should have no error on valid transform body', async () => { + const text = readJvTestAsset('transform-body/valid-transform-body.jv'); + + await parseAndValidateTransformBody(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(0); + }); + + it('should diagnose error on duplicate port names', async () => { + const text = readJvTestAsset('transform-body/invalid-duplicate-ports.jv'); + + await parseAndValidateTransformBody(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(2); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + `The transform port name "inputParam" needs to be unique.`, + expect.any(Object), + ); + }); + + it('should diagnose error on missing output assignment', async () => { + const text = readJvTestAsset( + 'transform-body/invalid-missing-output-assignment.jv', + ); + + await parseAndValidateTransformBody(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(2); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + `An output assignment is required for this port`, + expect.any(Object), + ); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'warning', + `This input port is never used`, + expect.any(Object), + ); + }); + + it('should diagnose error on multiple output assignments', async () => { + const text = readJvTestAsset( + 'transform-body/invalid-multiple-output-assignments.jv', + ); + + await parseAndValidateTransformBody(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(2); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + `At most one assignment per output port`, + expect.any(Object), + ); + }); + + it('should diagnose error on multiple output ports', async () => { + const text = readJvTestAsset( + 'transform-body/invalid-multiple-output-ports.jv', + ); + + await parseAndValidateTransformBody(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(2); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + `More than one output port is defined`, + expect.any(Object), + ); + }); + + it('should diagnose error on missing output ports', async () => { + const text = readJvTestAsset( + 'transform-body/invalid-missing-output-port.jv', + ); + + await parseAndValidateTransformBody(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(2); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + `There has to be a single output port`, + expect.any(Object), + ); + }); + + it('should not diagnose on multiple input ports', async () => { + const text = readJvTestAsset( + 'transform-body/valid-multiple-input-ports.jv', + ); + + await parseAndValidateTransformBody(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'warning', + `This input port is never used`, + expect.any(Object), + ); + }); + + it('should not diagnose on missing input ports', async () => { + const text = readJvTestAsset('transform-body/valid-missing-input-port.jv'); + + await parseAndValidateTransformBody(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(0); + }); +}); diff --git a/libs/language-server/src/lib/validation/checks/transform-body.ts b/libs/language-server/src/lib/validation/checks/transform-body.ts new file mode 100644 index 00000000..a05292da --- /dev/null +++ b/libs/language-server/src/lib/validation/checks/transform-body.ts @@ -0,0 +1,164 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** + * See https://jvalue.github.io/jayvee/docs/dev/guides/working-with-the-ast/ for why the following ESLint rule is disabled for this file. + */ +/* eslint-disable @typescript-eslint/no-unnecessary-condition */ + +import { + type TransformBody, + type TransformPortDefinition, + isTransformPortDefinition, +} from '../../ast/generated/ast'; +import { type JayveeValidationProps } from '../validation-registry'; +import { checkUniqueNames } from '../validation-util'; + +import { + extractReferenceLiterals, + validateTransformOutputAssignment, +} from './transform-output-assigment'; + +export function validateTransformBody( + transformBody: TransformBody, + props: JayveeValidationProps, +): void { + checkUniqueNames( + transformBody.ports, + props.validationContext, + 'transform port', + ); + checkUniqueOutputAssignments(transformBody, props); + + checkSingleOutputPort(transformBody, props); + + checkAreInputsUsed(transformBody, props); + + for (const property of transformBody.outputAssignments) { + validateTransformOutputAssignment(property, props); + } +} + +function checkUniqueOutputAssignments( + transformBody: TransformBody, + props: JayveeValidationProps, +): void { + const assignedOutputPorts = transformBody?.outputAssignments ?? []; + const definedOutputPorts = + transformBody?.ports?.filter((x) => x?.kind === 'to') ?? []; + + for (const definedOutputPort of definedOutputPorts) { + const usedInAssignments = assignedOutputPorts.filter( + (x) => x?.outPortName?.ref?.name === definedOutputPort.name, + ); + + if (usedInAssignments.length === 0) { + props.validationContext.accept( + 'error', + 'An output assignment is required for this port', + { node: definedOutputPort, property: 'name' }, + ); + } + + if (usedInAssignments.length > 1) { + usedInAssignments.forEach((usedAssignment) => { + props.validationContext.accept( + 'error', + 'At most one assignment per output port', + { + node: usedAssignment, + property: 'outPortName', + }, + ); + }); + } + } +} + +function checkSingleOutputPort( + transformBody: TransformBody, + props: JayveeValidationProps, +): void { + const ports = transformBody.ports?.filter((x) => x.kind === 'to'); + if (ports === undefined) { + return undefined; + } + + if (ports.length > 1) { + ports.forEach((port) => { + props.validationContext.accept( + 'error', + `More than one output port is defined`, + { + node: port, + }, + ); + }); + } + + if (ports.length === 0) { + props.validationContext.accept( + 'error', + `There has to be a single output port`, + { + node: transformBody.$container, + property: 'name', + }, + ); + } +} + +function checkAreInputsUsed( + transformBody: TransformBody, + props: JayveeValidationProps, +): void { + const inputs = transformBody.ports?.filter((x) => x.kind === 'from'); + const outputAssignments = transformBody?.outputAssignments; + if (inputs === undefined || outputAssignments === undefined) { + return undefined; + } + + const referencedPorts: TransformPortDefinition[] = []; + outputAssignments.forEach((outputAssignment) => { + const referenceLiterals = extractReferenceLiterals( + outputAssignment?.expression, + ); + + referencedPorts.push( + ...referenceLiterals + .map((x) => x?.value?.ref) + .filter(isTransformPortDefinition), + ); + }); + + const referencedPortNames = referencedPorts.map((x) => x?.name); + inputs.forEach((input) => { + if (input.name === undefined) { + return; + } + + if (!referencedPortNames.includes(input.name)) { + if (isOutputPortComplete(input)) { + props.validationContext.accept( + 'warning', + 'This input port is never used', + { + node: input, + }, + ); + } + } + }); +} + +function isOutputPortComplete( + portDefinition: TransformPortDefinition, +): boolean { + const valueType = portDefinition?.valueType?.reference?.ref; + if (valueType === undefined) { + return false; + } + + return valueType?.name !== undefined; +} diff --git a/libs/language-server/src/lib/validation/checks/transform-output-assigment.ts b/libs/language-server/src/lib/validation/checks/transform-output-assigment.ts new file mode 100644 index 00000000..7e87d402 --- /dev/null +++ b/libs/language-server/src/lib/validation/checks/transform-output-assigment.ts @@ -0,0 +1,130 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** + * See https://jvalue.github.io/jayvee/docs/dev/guides/working-with-the-ast/ for why the following ESLint rule is disabled for this file. + */ +/* eslint-disable @typescript-eslint/no-unnecessary-condition */ + +import { assertUnreachable } from 'langium'; + +import { inferExpressionType } from '../../ast/expressions/type-inference'; +import { + type Expression, + type ReferenceLiteral, + type TransformOutputAssignment, + isBinaryExpression, + isExpressionLiteral, + isReferenceLiteral, + isTernaryExpression, + isTransformPortDefinition, + isUnaryExpression, +} from '../../ast/generated/ast'; +import { type JayveeValidationProps } from '../validation-registry'; +import { checkExpressionSimplification } from '../validation-util'; + +export function validateTransformOutputAssignment( + outputAssignment: TransformOutputAssignment, + props: JayveeValidationProps, +): void { + checkOutputValueTyping(outputAssignment, props); + checkOutputNotInAssignmentExpression(outputAssignment, props); +} + +function checkOutputValueTyping( + outputAssignment: TransformOutputAssignment, + props: JayveeValidationProps, +): void { + const assignmentExpression = outputAssignment?.expression; + if (assignmentExpression === undefined) { + return; + } + + const outputType = outputAssignment?.outPortName?.ref?.valueType; + if (outputType === undefined) { + return; + } + + const inferredType = inferExpressionType( + assignmentExpression, + props.validationContext, + props.valueTypeProvider, + props.wrapperFactories, + ); + if (inferredType === undefined) { + return; + } + + const expectedType = props.wrapperFactories.ValueType.wrap(outputType); + if (expectedType === undefined) { + return; + } + + if (!inferredType.isConvertibleTo(expectedType)) { + props.validationContext.accept( + 'error', + `The value needs to be of type ${expectedType.getName()} but is of type ${inferredType.getName()}`, + { + node: assignmentExpression, + }, + ); + return; + } + + checkExpressionSimplification(assignmentExpression, props); +} + +function checkOutputNotInAssignmentExpression( + outputAssignment: TransformOutputAssignment, + props: JayveeValidationProps, +): void { + const referenceLiterals = extractReferenceLiterals( + outputAssignment?.expression, + ); + + referenceLiterals.forEach((referenceLiteral) => { + const referenced = referenceLiteral?.value?.ref; + if (!isTransformPortDefinition(referenced)) { + return; + } + if (referenced?.kind === 'to') { + props.validationContext.accept( + 'error', + 'Output ports are not allowed in this expression', + { + node: referenceLiteral, + }, + ); + } + }); +} + +export function extractReferenceLiterals( + expression: Expression | undefined, +): ReferenceLiteral[] { + if (expression === undefined) { + return []; + } + + if (isExpressionLiteral(expression)) { + if (isReferenceLiteral(expression)) { + return [expression]; + } + return []; + } else if (isTernaryExpression(expression)) { + return [ + ...extractReferenceLiterals(expression.first), + ...extractReferenceLiterals(expression.second), + ...extractReferenceLiterals(expression.third), + ]; + } else if (isBinaryExpression(expression)) { + return [ + ...extractReferenceLiterals(expression.left), + ...extractReferenceLiterals(expression.right), + ]; + } else if (isUnaryExpression(expression)) { + return extractReferenceLiterals(expression.expression); + } + assertUnreachable(expression); +} diff --git a/libs/language-server/src/lib/validation/checks/transform-output-assignment.spec.ts b/libs/language-server/src/lib/validation/checks/transform-output-assignment.spec.ts new file mode 100644 index 00000000..03e0e89c --- /dev/null +++ b/libs/language-server/src/lib/validation/checks/transform-output-assignment.spec.ts @@ -0,0 +1,113 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type AstNode, + type AstNodeLocator, + type LangiumDocument, +} from 'langium'; +import { NodeFileSystem } from 'langium/node'; +import { vi } from 'vitest'; + +import { + type JayveeServices, + type TransformOutputAssignment, + createJayveeServices, +} from '../../../lib'; +import { + type ParseHelperOptions, + createJayveeValidationProps, + expectNoParserAndLexerErrors, + parseHelper, + readJvTestAssetHelper, + validationAcceptorMockImpl, +} from '../../../test'; + +import { validateTransformOutputAssignment } from './transform-output-assigment'; + +describe('Validation of TransformOutputAssignment', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + const validationAcceptorMock = vi.fn(validationAcceptorMockImpl); + + let locator: AstNodeLocator; + let services: JayveeServices; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../../test/assets/', + ); + + async function parseAndValidateTransformOutputAssignment(input: string) { + const document = await parse(input); + expectNoParserAndLexerErrors(document); + + const transformOutputAssignment = + locator.getAstNode<TransformOutputAssignment>( + document.parseResult.value, + 'transforms@0/body/outputAssignments@0', + ) as TransformOutputAssignment; + + validateTransformOutputAssignment( + transformOutputAssignment, + createJayveeValidationProps(validationAcceptorMock, services), + ); + } + + beforeAll(() => { + // Create language services + services = createJayveeServices(NodeFileSystem).Jayvee; + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + afterEach(() => { + // Reset mock + validationAcceptorMock.mockReset(); + }); + + it('should have no error on valid transform output assignment', async () => { + const text = readJvTestAsset( + 'transform-output-assignment/valid-transform-output-assignment.jv', + ); + + await parseAndValidateTransformOutputAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(0); + }); + + it('should diagnose error on invalid type', async () => { + const text = readJvTestAsset( + 'transform-output-assignment/invalid-invalid-type.jv', + ); + + await parseAndValidateTransformOutputAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + `The value needs to be of type integer but is of type decimal`, + expect.any(Object), + ); + }); + + it('should diagnose error on output port used in assignment', async () => { + const text = readJvTestAsset( + 'transform-output-assignment/invalid-output-port-in-assignment.jv', + ); + + await parseAndValidateTransformOutputAssignment(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + `Output ports are not allowed in this expression`, + expect.any(Object), + ); + }); +}); diff --git a/libs/language-server/src/lib/validation/checks/typed-constraint-definition.spec.ts b/libs/language-server/src/lib/validation/checks/typed-constraint-definition.spec.ts new file mode 100644 index 00000000..fbf31496 --- /dev/null +++ b/libs/language-server/src/lib/validation/checks/typed-constraint-definition.spec.ts @@ -0,0 +1,100 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type AstNode, + type AstNodeLocator, + type LangiumDocument, +} from 'langium'; +import { NodeFileSystem } from 'langium/node'; +import { vi } from 'vitest'; + +import { + type JayveeServices, + type TypedConstraintDefinition, + createJayveeServices, + initializeWorkspace, +} from '../../../lib'; +import { + type ParseHelperOptions, + createJayveeValidationProps, + expectNoParserAndLexerErrors, + parseHelper, + readJvTestAssetHelper, + validationAcceptorMockImpl, +} from '../../../test'; + +import { validateTypedConstraintDefinition } from './typed-constraint-definition'; + +describe('Validation of ConstraintDefinition (typed syntax)', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + const validationAcceptorMock = vi.fn(validationAcceptorMockImpl); + + let locator: AstNodeLocator; + let services: JayveeServices; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../../test/assets/', + ); + + async function parseAndValidateTypedConstraintDefinition(input: string) { + const document = await parse(input); + expectNoParserAndLexerErrors(document); + + const typedConstraint = locator.getAstNode<TypedConstraintDefinition>( + document.parseResult.value, + 'constraints@0', + ) as TypedConstraintDefinition; + + validateTypedConstraintDefinition( + typedConstraint, + createJayveeValidationProps(validationAcceptorMock, services), + ); + } + + beforeAll(async () => { + // Create language services + services = createJayveeServices(NodeFileSystem).Jayvee; + await initializeWorkspace(services); + + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + afterEach(() => { + // Reset mock + validationAcceptorMock.mockReset(); + }); + + it('should have no error on valid typed constraint', async () => { + const text = readJvTestAsset( + 'typed-constraint-definition/valid-typed-constraint.jv', + ); + + await parseAndValidateTypedConstraintDefinition(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(0); + }); + + it('should diagnose error on unknown constraint type', async () => { + const text = readJvTestAsset( + 'typed-constraint-definition/invalid-unknown-constraint-type.jv', + ); + + await parseAndValidateTypedConstraintDefinition(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + `Unknown constraint type 'UnknownConstraint'`, + expect.any(Object), + ); + }); +}); diff --git a/libs/language-server/src/lib/validation/checks/typed-constraint-definition.ts b/libs/language-server/src/lib/validation/checks/typed-constraint-definition.ts new file mode 100644 index 00000000..a972db40 --- /dev/null +++ b/libs/language-server/src/lib/validation/checks/typed-constraint-definition.ts @@ -0,0 +1,42 @@ +/* eslint-disable @typescript-eslint/no-unnecessary-condition */ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** + * See https://jvalue.github.io/jayvee/docs/dev/guides/working-with-the-ast/ for why the following ESLint rule is disabled for this file. + */ + +import { type TypedConstraintDefinition } from '../../ast/generated/ast'; +import { ConstraintTypeWrapper } from '../../ast/wrappers/typed-object/constrainttype-wrapper'; +import { type JayveeValidationProps } from '../validation-registry'; + +export function validateTypedConstraintDefinition( + constraint: TypedConstraintDefinition, + props: JayveeValidationProps, +): void { + checkConstraintType(constraint, props); + // TODO: add custom validations +} + +function checkConstraintType( + constraint: TypedConstraintDefinition, + props: JayveeValidationProps, +): void { + const constraintType = constraint?.type; + if (constraintType === undefined) { + return undefined; + } + + const canCreateWrapper = ConstraintTypeWrapper.canBeWrapped(constraintType); + if (!canCreateWrapper) { + props.validationContext.accept( + 'error', + `Unknown constraint type '${constraintType.$refText ?? ''}'`, + { + node: constraint, + property: 'type', + }, + ); + } +} diff --git a/libs/language-server/src/lib/validation/checks/value-type-definition.spec.ts b/libs/language-server/src/lib/validation/checks/value-type-definition.spec.ts new file mode 100644 index 00000000..ba6d5296 --- /dev/null +++ b/libs/language-server/src/lib/validation/checks/value-type-definition.spec.ts @@ -0,0 +1,142 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type AstNode, + type AstNodeLocator, + type LangiumDocument, +} from 'langium'; +import { NodeFileSystem } from 'langium/node'; +import { vi } from 'vitest'; + +import { + type JayveeServices, + type ValuetypeDefinition, + createJayveeServices, +} from '../../../lib'; +import { + type ParseHelperOptions, + createJayveeValidationProps, + expectNoParserAndLexerErrors, + parseHelper, + readJvTestAssetHelper, + validationAcceptorMockImpl, +} from '../../../test'; + +import { validateValueTypeDefinition } from './value-type-definition'; + +describe('Validation of ValuetypeDefinition', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + const validationAcceptorMock = vi.fn(validationAcceptorMockImpl); + + let locator: AstNodeLocator; + let services: JayveeServices; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../../test/assets/', + ); + + async function parseAndValidateValuetypeDefinition(input: string) { + const document = await parse(input); + expectNoParserAndLexerErrors(document); + + const valueTypeDefinition = locator.getAstNode<ValuetypeDefinition>( + document.parseResult.value, + 'valueTypes@0', + ) as ValuetypeDefinition; + + validateValueTypeDefinition( + valueTypeDefinition, + createJayveeValidationProps(validationAcceptorMock, services), + ); + } + + beforeAll(() => { + // Create language services + services = createJayveeServices(NodeFileSystem).Jayvee; + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + afterEach(() => { + // Reset mock + validationAcceptorMock.mockReset(); + }); + + it('should have no error on empty constraint list', async () => { + const text = readJvTestAsset( + 'value-type-definition/valid-value-type-definition.jv', + ); + + await parseAndValidateValuetypeDefinition(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(0); + }); + + it('should diagnose error on supertype cycle', async () => { + const text = readJvTestAsset( + 'value-type-definition/invalid-supertype-cycle.jv', + ); + + await parseAndValidateValuetypeDefinition(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + `Could not construct this value type since there is a cycle in the (transitive) "oftype" relation.`, + expect.any(Object), + ); + }); + + it('should diagnose error on invalid constraints item', async () => { + const text = readJvTestAsset( + 'value-type-definition/invalid-invalid-constraints-item.jv', + ); + + await parseAndValidateValuetypeDefinition(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + `The value needs to be of type Collection<Constraint> but is of type Collection<boolean>`, + expect.any(Object), + ); + }); + + it('should diagnose error on invalid constraint type for value type', async () => { + const text = readJvTestAsset( + 'value-type-definition/invalid-invalid-constraint-type-for-value-type.jv', + ); + + await parseAndValidateValuetypeDefinition(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + `This value type ValueType is not convertible to the type integer of the constraint "Constraint"`, + expect.any(Object), + ); + }); + + it('should diagnose error on duplicate generic on value type', async () => { + const text = readJvTestAsset( + 'value-type-definition/invalid-duplicate-generic.jv', + ); + + await parseAndValidateValuetypeDefinition(text); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(2); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + `Generic parameter T is not unique`, + expect.any(Object), + ); + }); +}); diff --git a/libs/language-server/src/lib/validation/checks/value-type-definition.ts b/libs/language-server/src/lib/validation/checks/value-type-definition.ts new file mode 100644 index 00000000..daa60be6 --- /dev/null +++ b/libs/language-server/src/lib/validation/checks/value-type-definition.ts @@ -0,0 +1,200 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** + * See https://jvalue.github.io/jayvee/docs/dev/guides/working-with-the-ast/ for why the following ESLint rule is disabled for this file. + */ +/* eslint-disable @typescript-eslint/no-unnecessary-condition */ + +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; + +import { assertUnreachable } from 'langium'; + +import { + type CollectionLiteral, + type ConstraintDefinition, + type ValueType, + evaluateExpression, + inferExpressionType, + isExpressionConstraintDefinition, + isTypedConstraintDefinition, +} from '../../ast'; +import { + type ValuetypeDefinition, + type ValuetypeGenericDefinition, +} from '../../ast/generated/ast'; +import { type JayveeValidationProps } from '../validation-registry'; + +export function validateValueTypeDefinition( + valueType: ValuetypeDefinition, + props: JayveeValidationProps, +): void { + checkSupertypeCycle(valueType, props); + checkConstraintsCollectionValues(valueType, props); + checkGenericsHaveNoDuplicate(valueType, props); +} + +function checkSupertypeCycle( + valueTypeDefinition: ValuetypeDefinition, + props: JayveeValidationProps, +): void { + const hasCycle = + props.wrapperFactories.ValueType.wrap( + valueTypeDefinition, + )?.hasSupertypeCycle() ?? false; + if (hasCycle) { + assert(!valueTypeDefinition.isBuiltin); + props.validationContext.accept( + 'error', + 'Could not construct this value type since there is a cycle in the (transitive) "oftype" relation.', + { + node: valueTypeDefinition, + property: 'type', + }, + ); + } +} + +function checkConstraintsCollectionValues( + valueType: ValuetypeDefinition, + props: JayveeValidationProps, +): void { + const constraintCollection = valueType?.constraints; + if (constraintCollection === undefined) { + return; + } + + const inferredCollectionType = inferExpressionType( + constraintCollection, + props.validationContext, + props.valueTypeProvider, + props.wrapperFactories, + ); + const expectedType = props.valueTypeProvider.createCollectionValueTypeOf( + props.valueTypeProvider.Primitives.Constraint, + ); + if (inferredCollectionType === undefined) { + return; + } + if (!inferredCollectionType.isConvertibleTo(expectedType)) { + props.validationContext.accept( + 'error', + `The value needs to be of type ${expectedType.getName()} but is of type ${inferredCollectionType.getName()}`, + { + node: constraintCollection, + }, + ); + return; + } + + const constraints = evaluateExpression( + constraintCollection, + props.evaluationContext, + props.wrapperFactories, + props.validationContext, + ); + assert(expectedType.isInternalValueRepresentation(constraints)); + + constraints.forEach((constraint, index) => { + checkConstraintMatchesValuetype( + valueType, + constraint, + constraintCollection, + index, + props, + ); + }); +} + +function checkConstraintMatchesValuetype( + valueTypeDefinition: ValuetypeDefinition, + constraint: ConstraintDefinition, + diagnosticNode: CollectionLiteral, + diagnosticIndex: number, + props: JayveeValidationProps, +): void { + const actualValuetype = + props.wrapperFactories.ValueType.wrap(valueTypeDefinition); + const compatibleValuetype = getCompatibleValuetype(constraint, props); + + if (actualValuetype === undefined || compatibleValuetype === undefined) { + return; + } + + if (!actualValuetype.isConvertibleTo(compatibleValuetype)) { + props.validationContext.accept( + 'error', + `This value type ${actualValuetype.getName()} is not convertible to the type ${compatibleValuetype.getName()} of the constraint "${ + constraint.name + }"`, + { + node: diagnosticNode, + property: 'values', + index: diagnosticIndex, + }, + ); + } +} + +function getCompatibleValuetype( + constraint: ConstraintDefinition, + props: JayveeValidationProps, +): ValueType | undefined { + if (isTypedConstraintDefinition(constraint)) { + if (props.wrapperFactories.ConstraintType.canWrap(constraint.type)) { + return undefined; + } + return props.wrapperFactories.ConstraintType.wrap(constraint.type).on; + } else if (isExpressionConstraintDefinition(constraint)) { + return props.wrapperFactories.ValueType.wrap(constraint?.valueType); + } + assertUnreachable(constraint); +} + +function checkGenericsHaveNoDuplicate( + valueTypeDefinition: ValuetypeDefinition, + props: JayveeValidationProps, +): void { + const generics = valueTypeDefinition.genericDefinition?.generics; + if (generics === undefined) { + return; + } + + const duplicates = getDuplicateGenerics(generics); + + duplicates.forEach((generic) => { + props.validationContext.accept( + 'error', + `Generic parameter ${generic.name} is not unique`, + { + node: generic, + property: 'name', + }, + ); + }); +} + +function getDuplicateGenerics( + generics: ValuetypeGenericDefinition[], +): ValuetypeGenericDefinition[] { + const countPerGenericName = generics + .map((generic) => { + return { name: generic.name, count: 1 }; + }) + .reduce((result: Record<string, number>, current) => { + const currentName = current.name; + result[currentName] = (result[currentName] ?? 0) + 1; + return result; + }, {}); + const duplicateGenericNames = Object.entries(countPerGenericName) + .filter(([, occurences]) => occurences > 1) + .map(([generic]) => generic); + + const duplicates: ValuetypeGenericDefinition[] = []; + duplicateGenericNames.forEach((genericName) => { + duplicates.push(...generics.filter((x) => x.name === genericName)); + }); + return duplicates; +} diff --git a/libs/language-server/src/lib/validation/checks/value-type-reference.spec.ts b/libs/language-server/src/lib/validation/checks/value-type-reference.spec.ts new file mode 100644 index 00000000..96f5fd49 --- /dev/null +++ b/libs/language-server/src/lib/validation/checks/value-type-reference.spec.ts @@ -0,0 +1,300 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; + +import { + type AstNode, + type AstNodeLocator, + type LangiumDocument, +} from 'langium'; +import { NodeFileSystem } from 'langium/node'; +import { vi } from 'vitest'; + +import { + type JayveeServices, + type ValueTypeReference, + type ValuetypeDefinition, + createJayveeServices, +} from '../..'; +import { + type ParseHelperOptions, + createJayveeValidationProps, + expectNoParserAndLexerErrors, + parseHelper, + readJvTestAssetHelper, + validationAcceptorMockImpl, +} from '../../../test'; + +import { validateValueTypeReference } from './value-type-reference'; + +describe('Validation of ValueTypeReference', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + const validationAcceptorMock = vi.fn(validationAcceptorMockImpl); + + let locator: AstNodeLocator; + let services: JayveeServices; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../../test/assets/', + ); + + async function parseValueTypeReferencesFromValuetypes(input: string) { + const document = await parse(input); + expectNoParserAndLexerErrors(document); + + const valueTypeReferences: ValueTypeReference[] = []; + + let valueTypeDefinition: ValuetypeDefinition | undefined; + let i = 0; + do { + valueTypeDefinition = locator.getAstNode<ValuetypeDefinition>( + document.parseResult.value, + `valueTypes@${i}`, + ); + if (valueTypeDefinition !== undefined) { + const valueTypeRef = valueTypeDefinition.type; + assert(valueTypeRef !== undefined); + valueTypeReferences.push(valueTypeRef); + } + ++i; + } while (valueTypeDefinition !== undefined); + + return valueTypeReferences; + } + + beforeAll(() => { + // Create language services + services = createJayveeServices(NodeFileSystem).Jayvee; + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + afterEach(() => { + // Reset mock + validationAcceptorMock.mockReset(); + }); + + it('should have no error on reference to non-generic value type', async () => { + const text = readJvTestAsset( + 'value-type-reference/valid-reference-to-non-generic-value-type.jv', + ); + + (await parseValueTypeReferencesFromValuetypes(text)).forEach( + (valueTypeRef) => { + validateValueTypeReference( + valueTypeRef, + createJayveeValidationProps(validationAcceptorMock, services), + ); + }, + ); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(0); + }); + + it('should have no error on reference to generic value type with single generic', async () => { + const text = readJvTestAsset( + 'value-type-reference/valid-reference-to-single-generic-value-type.jv', + ); + + (await parseValueTypeReferencesFromValuetypes(text)).forEach( + (valueTypeRef) => { + validateValueTypeReference( + valueTypeRef, + createJayveeValidationProps(validationAcceptorMock, services), + ); + }, + ); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(0); + }); + + it('should have no error on reference to generic value type with multiple generics', async () => { + const text = readJvTestAsset( + 'value-type-reference/valid-reference-to-multiple-generic-value-type.jv', + ); + + (await parseValueTypeReferencesFromValuetypes(text)).forEach( + (valueTypeRef) => { + validateValueTypeReference( + valueTypeRef, + createJayveeValidationProps(validationAcceptorMock, services), + ); + }, + ); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(0); + }); + + it('should diagnose error on reference to generic value type with missing generic parameters', async () => { + const text = readJvTestAsset( + 'value-type-reference/invalid-reference-missing-generic.jv', + ); + + (await parseValueTypeReferencesFromValuetypes(text)).forEach( + (valueTypeRef) => { + validateValueTypeReference( + valueTypeRef, + createJayveeValidationProps(validationAcceptorMock, services), + ); + }, + ); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + `The referenced value type ValueType requires 2 generic parameters but found 0.`, + expect.any(Object), + ); + }); + + it('should diagnose error on reference to generic value type with too few generic parameters', async () => { + const text = readJvTestAsset( + 'value-type-reference/invalid-reference-too-few-generic-parameters.jv', + ); + + (await parseValueTypeReferencesFromValuetypes(text)).forEach( + (valueTypeRef) => { + validateValueTypeReference( + valueTypeRef, + createJayveeValidationProps(validationAcceptorMock, services), + ); + }, + ); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + `The referenced value type ValueType requires 2 generic parameters but found 1.`, + expect.any(Object), + ); + }); + + it('should diagnose error on reference to generic value type with too few generic parameters', async () => { + const text = readJvTestAsset( + 'value-type-reference/invalid-reference-too-many-generic-parameters.jv', + ); + + (await parseValueTypeReferencesFromValuetypes(text)).forEach( + (valueTypeRef) => { + validateValueTypeReference( + valueTypeRef, + createJayveeValidationProps(validationAcceptorMock, services), + ); + }, + ); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + `The referenced value type ValueType requires 2 generic parameters but found 3.`, + expect.any(Object), + ); + }); + + it('should diagnose error on reference to non-generic value type with generic parameters', async () => { + const text = readJvTestAsset( + 'value-type-reference/invalid-reference-too-non-generic-with-generic-parameters.jv', + ); + + (await parseValueTypeReferencesFromValuetypes(text)).forEach( + (valueTypeRef) => { + validateValueTypeReference( + valueTypeRef, + createJayveeValidationProps(validationAcceptorMock, services), + ); + }, + ); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + `The referenced value type ValueType requires 0 generic parameters but found 1.`, + expect.any(Object), + ); + }); + + it('should diagnose error on reference to non-referenceable value type', async () => { + const text = readJvTestAsset( + 'value-type-reference/invalid-reference-to-non-referenceable-value-type-in-value-type.jv', + ); + + (await parseValueTypeReferencesFromValuetypes(text)).forEach( + (valueTypeRef) => { + validateValueTypeReference( + valueTypeRef, + createJayveeValidationProps(validationAcceptorMock, services), + ); + }, + ); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(2); + expect(validationAcceptorMock).toHaveBeenNthCalledWith( + 1, + 'error', + `Value type Constraint cannot be referenced in this context`, + expect.any(Object), + ); + expect(validationAcceptorMock).toHaveBeenNthCalledWith( + 2, + 'error', + `Value type Regex cannot be referenced in this context`, + expect.any(Object), + ); + }); + + it('should diagnose error on reference to non-referenceable value-type in a block', async () => { + const text = readJvTestAsset( + 'value-type-reference/invalid-reference-to-non-referenceable-value-type-in-block.jv', + ); + + const document = await parse(text); + expectNoParserAndLexerErrors(document); + const valueTypeRef = locator.getAstNode<ValueTypeReference>( + document.parseResult.value, + `pipelines@0/blocks@0/body/properties@0/value/values@0/value/type`, + ); + assert(valueTypeRef !== undefined); + + validateValueTypeReference( + valueTypeRef, + createJayveeValidationProps(validationAcceptorMock, services), + ); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(1); + expect(validationAcceptorMock).toHaveBeenCalledWith( + 'error', + `Value type Constraint cannot be referenced in this context`, + expect.any(Object), + ); + }); + + it('should not diagnose error on reference to non-referenceable value-type in a builtin block type', async () => { + const text = readJvTestAsset( + 'value-type-reference/valid-reference-to-non-referenceable-value-type-in-builtin-block-type.jv', + ); + + const document = await parse(text); + expectNoParserAndLexerErrors(document); + const valueTypeRef = locator.getAstNode<ValueTypeReference>( + document.parseResult.value, + `blockTypes@0/properties@0/valueType`, + ); + assert(valueTypeRef !== undefined); + + validateValueTypeReference( + valueTypeRef, + createJayveeValidationProps(validationAcceptorMock, services), + ); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(0); + }); +}); diff --git a/libs/language-server/src/lib/validation/checks/value-type-reference.ts b/libs/language-server/src/lib/validation/checks/value-type-reference.ts new file mode 100644 index 00000000..35a16fb8 --- /dev/null +++ b/libs/language-server/src/lib/validation/checks/value-type-reference.ts @@ -0,0 +1,74 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type ValueTypeReference, + isBuiltinBlockTypeDefinition, + isBuiltinConstrainttypeDefinition, +} from '../../ast/generated/ast'; +import { type JayveeValidationProps } from '../validation-registry'; + +export function validateValueTypeReference( + valueTypeRef: ValueTypeReference, + props: JayveeValidationProps, +): void { + checkGenericsMatchDefinition(valueTypeRef, props); + checkIsValueTypeReferenceable(valueTypeRef, props); +} + +function checkGenericsMatchDefinition( + valueTypeRef: ValueTypeReference, + props: JayveeValidationProps, +): void { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + const valueTypeDefinition = valueTypeRef.reference?.ref; + if (valueTypeDefinition === undefined) { + return; + } + + const requiredGenerics = + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + valueTypeDefinition.genericDefinition?.generics?.length ?? 0; + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + const givenGenerics = valueTypeRef?.genericRefs?.length ?? 0; + + if (givenGenerics !== requiredGenerics) { + props.validationContext.accept( + 'error', + `The referenced value type ${valueTypeDefinition.name} requires ${requiredGenerics} generic parameters but found ${givenGenerics}.`, + { + node: valueTypeRef, + }, + ); + } +} + +function checkIsValueTypeReferenceable( + valueTypeRef: ValueTypeReference, + props: JayveeValidationProps, +): void { + const valueType = props.wrapperFactories.ValueType.wrap(valueTypeRef); + if (valueType === undefined) { + return; + } + + const isUsedInBuiltinDefinition = + isBuiltinBlockTypeDefinition(valueTypeRef.$container.$container) || + isBuiltinConstrainttypeDefinition(valueTypeRef.$container.$container); + if (isUsedInBuiltinDefinition) { + return; + } + + if (valueType.isReferenceableByUser()) { + return; + } + + props.validationContext.accept( + 'error', + `Value type ${valueType.getName()} cannot be referenced in this context`, + { + node: valueTypeRef, + }, + ); +} diff --git a/libs/language-server/src/lib/validation/docs-constraint-examples.spec.ts b/libs/language-server/src/lib/validation/docs-constraint-examples.spec.ts new file mode 100644 index 00000000..62f0473c --- /dev/null +++ b/libs/language-server/src/lib/validation/docs-constraint-examples.spec.ts @@ -0,0 +1,39 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { NodeFileSystem } from 'langium/node'; + +import { validationHelper } from '../../test/langium-utils'; +import { getAllBuiltinConstraintTypes } from '../ast'; +import { initializeWorkspace } from '../builtin-library'; +import { createJayveeServices } from '../jayvee-module'; + +describe('Validation of docs examples of ConstraintTypes', () => { + it('should have no validation errors', async () => { + // Create language services + const services = createJayveeServices(NodeFileSystem).Jayvee; + + await initializeWorkspace(services); + + // Create validation helper for language services + const validate = validationHelper(services); + + const allConstraintTypes = getAllBuiltinConstraintTypes( + services.shared.workspace.LangiumDocuments, + services.WrapperFactories, + ); + expect(allConstraintTypes.length).toBeGreaterThan(0); + + await Promise.all( + allConstraintTypes.map(async (constraintType) => { + for (const example of constraintType.docs.examples ?? []) { + const validationResult = await validate(example.code); + const diagnostics = validationResult.diagnostics; + + expect(diagnostics).toHaveLength(0); + } + }), + ); + }); +}); diff --git a/libs/language-server/src/lib/validation/index.ts b/libs/language-server/src/lib/validation/index.ts new file mode 100644 index 00000000..cd82d93e --- /dev/null +++ b/libs/language-server/src/lib/validation/index.ts @@ -0,0 +1,7 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +export * from './validation-context'; +export * from './validation-registry'; +export * from './validation-util'; diff --git a/libs/language-server/src/lib/validation/validation-context.ts b/libs/language-server/src/lib/validation/validation-context.ts new file mode 100644 index 00000000..6379a72e --- /dev/null +++ b/libs/language-server/src/lib/validation/validation-context.ts @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type AstNode, + type DiagnosticInfo, + type ValidationAcceptor, +} from 'langium'; + +import { type OperatorTypeComputerRegistry } from '../ast/expressions/operator-registry'; + +export class ValidationContext { + private errorOccurred = false; + + constructor( + private readonly validationAcceptor: ValidationAcceptor, + public readonly typeComputerRegistry: OperatorTypeComputerRegistry, + ) {} + + accept: ValidationAcceptor = <N extends AstNode>( + severity: 'error' | 'warning' | 'info' | 'hint', + message: string, + info: DiagnosticInfo<N>, + ): void => { + if (severity === 'error') { + this.errorOccurred = true; + } + this.validationAcceptor(severity, message, info); + }; + + hasErrorOccurred(): boolean { + return this.errorOccurred; + } +} diff --git a/libs/language-server/src/lib/validation/validation-registry.ts b/libs/language-server/src/lib/validation/validation-registry.ts new file mode 100644 index 00000000..50656bf0 --- /dev/null +++ b/libs/language-server/src/lib/validation/validation-registry.ts @@ -0,0 +1,140 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type AstNode, + type MaybePromise, + type ValidationAcceptor, + type ValidationCheck, + ValidationRegistry, +} from 'langium'; + +import { + EvaluationContext, + type OperatorEvaluatorRegistry, + type OperatorTypeComputerRegistry, + type ValueTypeProvider, + type WrapperFactoryProvider, +} from '../ast'; +import { type JayveeAstType } from '../ast/generated/ast'; +import type { JayveeServices } from '../jayvee-module'; +import { type RuntimeParameterProvider } from '../services'; + +import { validateBlockDefinition } from './checks/block-definition'; +import { validateBlockTypeDefinition } from './checks/block-type-definition'; +import { validateColumnId } from './checks/column-id'; +import { validateCompositeBlockTypeDefinition } from './checks/composite-block-type-definition'; +import { validateExpressionConstraintDefinition } from './checks/expression-constraint-definition'; +import { validateJayveeModel } from './checks/jayvee-model'; +import { validatePipeDefinition } from './checks/pipe-definition'; +import { validatePipelineDefinition } from './checks/pipeline-definition'; +import { validatePropertyBody } from './checks/property-body'; +import { validateRangeLiteral } from './checks/range-literal'; +import { validateRegexLiteral } from './checks/regex-literal'; +import { validateTransformBody } from './checks/transform-body'; +import { validateTypedConstraintDefinition } from './checks/typed-constraint-definition'; +import { validateValueTypeDefinition } from './checks/value-type-definition'; +import { validateValueTypeReference } from './checks/value-type-reference'; +import { ValidationContext } from './validation-context'; + +/** + * Registry for validation checks. + */ +export class JayveeValidationRegistry extends ValidationRegistry { + private readonly runtimeParameterProvider; + private readonly typeComputerRegistry: OperatorTypeComputerRegistry; + private readonly operatorEvaluatorRegistry: OperatorEvaluatorRegistry; + private readonly wrapperFactories: WrapperFactoryProvider; + private readonly valueTypeProvider: ValueTypeProvider; + + constructor(services: JayveeServices) { + super(services); + + this.runtimeParameterProvider = services.RuntimeParameterProvider; + this.typeComputerRegistry = services.operators.TypeComputerRegistry; + this.operatorEvaluatorRegistry = services.operators.EvaluatorRegistry; + this.wrapperFactories = services.WrapperFactories; + this.valueTypeProvider = services.ValueTypeProvider; + + this.registerJayveeValidationChecks({ + BuiltinBlockTypeDefinition: validateBlockTypeDefinition, + BlockDefinition: validateBlockDefinition, + CompositeBlockTypeDefinition: validateCompositeBlockTypeDefinition, + ColumnId: validateColumnId, + TypedConstraintDefinition: validateTypedConstraintDefinition, + ExpressionConstraintDefinition: validateExpressionConstraintDefinition, + JayveeModel: validateJayveeModel, + PipeDefinition: validatePipeDefinition, + PipelineDefinition: validatePipelineDefinition, + PropertyBody: validatePropertyBody, + RangeLiteral: validateRangeLiteral, + RegexLiteral: validateRegexLiteral, + ValuetypeDefinition: validateValueTypeDefinition, + ValueTypeReference: validateValueTypeReference, + TransformBody: validateTransformBody, + }); + } + + registerJayveeValidationChecks(checksRecord: JayveeValidationChecks) { + for (const [type, check] of Object.entries(checksRecord)) { + const wrappedCheck = this.wrapJayveeValidationCheck( + check as JayveeValidationCheck, + this.runtimeParameterProvider, + this.typeComputerRegistry, + this.operatorEvaluatorRegistry, + this.wrapperFactories, + this.valueTypeProvider, + ); + + this.addEntry(type, { + category: 'fast', + check: this.wrapValidationException(wrappedCheck, this), + }); + } + } + + private wrapJayveeValidationCheck<T extends AstNode = AstNode>( + check: JayveeValidationCheck<T>, + runtimeParameterProvider: RuntimeParameterProvider, + typeComputerRegistry: OperatorTypeComputerRegistry, + operatorEvaluatorRegistry: OperatorEvaluatorRegistry, + wrapperFactories: WrapperFactoryProvider, + valueTypeProvider: ValueTypeProvider, + ): ValidationCheck<T> { + return (node: T, accept: ValidationAcceptor): MaybePromise<void> => { + const validationContext = new ValidationContext( + accept, + typeComputerRegistry, + ); + const evaluationContext = new EvaluationContext( + runtimeParameterProvider, + operatorEvaluatorRegistry, + valueTypeProvider, + ); + return check(node, { + validationContext: validationContext, + evaluationContext: evaluationContext, + wrapperFactories: wrapperFactories, + valueTypeProvider: valueTypeProvider, + }); + }; + } +} + +export type JayveeValidationChecks<T = JayveeAstType> = { + [K in keyof T]?: T[K] extends AstNode ? JayveeValidationCheck<T[K]> : never; +} & { + AstNode?: ValidationCheck<AstNode>; +}; + +export interface JayveeValidationProps { + validationContext: ValidationContext; + evaluationContext: EvaluationContext; + wrapperFactories: WrapperFactoryProvider; + valueTypeProvider: ValueTypeProvider; +} +export type JayveeValidationCheck<T extends AstNode = AstNode> = ( + node: T, + props: JayveeValidationProps, +) => MaybePromise<void>; diff --git a/libs/language-server/src/lib/validation/validation-util.ts b/libs/language-server/src/lib/validation/validation-util.ts new file mode 100644 index 00000000..5cd977dc --- /dev/null +++ b/libs/language-server/src/lib/validation/validation-util.ts @@ -0,0 +1,190 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; + +import { type AstNode, MultiMap, assertUnreachable } from 'langium'; + +import { + EvaluationStrategy, + type Expression, + evaluateExpression, + internalValueToString, + isBinaryExpression, + isExpressionLiteral, + isFreeVariableLiteral, + isTernaryExpression, + isUnaryExpression, + isValueLiteral, +} from '../ast'; + +import { type ValidationContext } from './validation-context'; +import { type JayveeValidationProps } from './validation-registry'; + +export type NamedAstNode = AstNode & { name: string }; + +export function checkUniqueNames( + nodes: NamedAstNode[], + context: ValidationContext, + nodeKind?: string, +): void { + const nodesByName = groupNodesByName(nodes); + + for (const [nodeName, nodes] of nodesByName.entriesGroupedByKey()) { + if (nodes.length > 1) { + for (const node of nodes) { + context.accept( + 'error', + `The ${ + nodeKind ?? node.$type.toLowerCase() + } name "${nodeName}" needs to be unique.`, + { + node, + property: 'name', + }, + ); + } + } + } +} + +function groupNodesByName( + nodes: NamedAstNode[], +): MultiMap<string, NamedAstNode> { + const nodesByName = new MultiMap<string, NamedAstNode>(); + + for (const node of nodes) { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (node?.name !== undefined) { + nodesByName.add(node.name, node); + } + } + + return nodesByName; +} + +export function checkExpressionSimplification( + expression: Expression, + props: JayveeValidationProps, +): void { + const simplifiableSubExpressions = collectSubExpressionsWithoutFreeVariables( + expression, + ).filter(isSimplifiableExpression); + + simplifiableSubExpressions.forEach((expression) => { + const evaluatedExpression = evaluateExpression( + expression, + props.evaluationContext, + props.wrapperFactories, + props.validationContext, + EvaluationStrategy.EXHAUSTIVE, + ); + assert(evaluatedExpression !== undefined); + + props.validationContext.accept( + 'info', + `The expression can be simplified to ${internalValueToString( + evaluatedExpression, + props.wrapperFactories, + )}`, + { node: expression }, + ); + }); +} + +function collectSubExpressionsWithoutFreeVariables( + expression: Expression | undefined, +): Expression[] { + if (expression === undefined) { + return []; + } + + if (isExpressionLiteral(expression)) { + if (isFreeVariableLiteral(expression)) { + return []; + } + return [expression]; + } else if (isUnaryExpression(expression)) { + const innerExpression = expression.expression; + const innerSubExpressions = + collectSubExpressionsWithoutFreeVariables(innerExpression); + + const innerExpressionHasNoFreeVariables = + innerSubExpressions.length === 1 && + innerSubExpressions[0] === innerExpression; + + if (innerExpressionHasNoFreeVariables) { + return [expression]; + } + return innerSubExpressions; + } else if (isBinaryExpression(expression)) { + const leftExpression = expression.left; + const rightExpression = expression.right; + const leftSubExpressions = + collectSubExpressionsWithoutFreeVariables(leftExpression); + const rightSubExpressions = + collectSubExpressionsWithoutFreeVariables(rightExpression); + + const leftExpressionHasNoFreeVariables = + leftSubExpressions.length === 1 && + leftSubExpressions[0] === leftExpression; + const rightExpressionHasNoFreeVariables = + rightSubExpressions.length === 1 && + rightSubExpressions[0] === rightExpression; + + if (leftExpressionHasNoFreeVariables && rightExpressionHasNoFreeVariables) { + return [expression]; + } + return [...leftSubExpressions, ...rightSubExpressions]; + } else if (isTernaryExpression(expression)) { + const firstExpression = expression.first; + const secondExpression = expression.second; + const thirdExpression = expression.third; + const firstSubExpressions = + collectSubExpressionsWithoutFreeVariables(firstExpression); + const secondSubExpressions = + collectSubExpressionsWithoutFreeVariables(secondExpression); + const thirdSubExpressions = + collectSubExpressionsWithoutFreeVariables(thirdExpression); + + const firstExpressionHasNoFreeVariables = + firstSubExpressions.length === 1 && + firstSubExpressions[0] === firstExpression; + const secondExpressionHasNoFreeVariables = + secondSubExpressions.length === 1 && + secondSubExpressions[0] === secondExpression; + const thirdExpressionHasNoFreeVariables = + thirdSubExpressions.length === 1 && + thirdSubExpressions[0] === thirdExpression; + + if ( + firstExpressionHasNoFreeVariables && + secondExpressionHasNoFreeVariables && + thirdExpressionHasNoFreeVariables + ) { + return [expression]; + } + return [ + ...firstSubExpressions, + ...secondSubExpressions, + ...thirdSubExpressions, + ]; + } + assertUnreachable(expression); +} + +function isSimplifiableExpression(expression: Expression): boolean { + return ( + !isExpressionLiteral(expression) && !isNegativeNumberExpression(expression) + ); +} + +function isNegativeNumberExpression(expression: Expression): boolean { + return ( + isUnaryExpression(expression) && + expression.operator === '-' && + isValueLiteral(expression.expression) + ); +} diff --git a/libs/language-server/src/lib/validation/validation-utils.spec.ts b/libs/language-server/src/lib/validation/validation-utils.spec.ts new file mode 100644 index 00000000..093d3053 --- /dev/null +++ b/libs/language-server/src/lib/validation/validation-utils.spec.ts @@ -0,0 +1,132 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + type AstNode, + type AstNodeLocator, + type LangiumDocument, +} from 'langium'; +import { NodeFileSystem } from 'langium/node'; +import { vi } from 'vitest'; + +import { + DefaultOperatorEvaluatorRegistry, + DefaultOperatorTypeComputerRegistry, + type JayveeServices, + type PropertyBody, + ValidationContext, + ValueTypeProvider, + WrapperFactoryProvider, + checkUniqueNames, + createJayveeServices, +} from '..'; +import { + type ParseHelperOptions, + expectNoParserAndLexerErrors, + parseHelper, + readJvTestAssetHelper, + validationAcceptorMockImpl, +} from '../../test'; + +describe('Validation of validation-utils', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise<LangiumDocument<AstNode>>; + + let locator: AstNodeLocator; + let services: JayveeServices; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../test/assets/', + ); + + async function parseAndExtractPropertyBody( + input: string, + ): Promise<PropertyBody> { + const document = await parse(input); + expectNoParserAndLexerErrors(document); + + return locator.getAstNode<PropertyBody>( + document.parseResult.value, + 'pipelines@0/blocks@0/body', + ) as PropertyBody; + } + + beforeAll(() => { + // Create language services + services = createJayveeServices(NodeFileSystem).Jayvee; + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + describe('Validation of checkUniqueNames', () => { + const validationAcceptorMock = vi.fn(validationAcceptorMockImpl); + + afterEach(() => { + // Reset mock + validationAcceptorMock.mockReset(); + }); + + it('should have no error', async () => { + const text = readJvTestAsset( + 'validation-utils/valid-distinct-property-names.jv', + ); + const valueTypeProvider = new ValueTypeProvider(); + + const propertyBody: PropertyBody = await parseAndExtractPropertyBody( + text, + ); + checkUniqueNames( + propertyBody.properties, + new ValidationContext( + validationAcceptorMock, + new DefaultOperatorTypeComputerRegistry( + valueTypeProvider, + new WrapperFactoryProvider( + new DefaultOperatorEvaluatorRegistry(), + valueTypeProvider, + ), + ), + ), + ); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(0); + }); + + it('should diagnose error on duplicate property names', async () => { + const text = readJvTestAsset( + 'validation-utils/invalid-duplicate-property-names.jv', + ); + const valueTypeProvider = new ValueTypeProvider(); + + const propertyBody: PropertyBody = await parseAndExtractPropertyBody( + text, + ); + checkUniqueNames( + propertyBody.properties, + new ValidationContext( + validationAcceptorMock, + new DefaultOperatorTypeComputerRegistry( + valueTypeProvider, + new WrapperFactoryProvider( + new DefaultOperatorEvaluatorRegistry(), + valueTypeProvider, + ), + ), + ), + ); + + expect(validationAcceptorMock).toHaveBeenCalledTimes(2); + expect(validationAcceptorMock).toHaveBeenNthCalledWith( + 2, + 'error', + `The propertyassignment name "textProperty" needs to be unique.`, + expect.any(Object), + ); + }); + }); +}); diff --git a/libs/language-server/src/stdlib/CSVExtractor.jv b/libs/language-server/src/stdlib/CSVExtractor.jv new file mode 100644 index 00000000..066a1bbf --- /dev/null +++ b/libs/language-server/src/stdlib/CSVExtractor.jv @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** +* A CSVExtractor extracts a file from a given URL and interprets it as CSV +*/ +composite blocktype CSVExtractor { + property url oftype text; + property delimiter oftype text: ','; + property enclosing oftype text: ''; + property enclosingEscape oftype text: ''; + property encoding oftype text: 'utf-8'; + + input inputPort oftype None; + output outputPort oftype Sheet; + + inputPort + -> FileExtractor + -> FileCSVInterpreter + -> outputPort; + + block FileExtractor oftype HttpExtractor { + url: url; + } + + block FileCSVInterpreter oftype CSVFileInterpreter { + delimiter: delimiter; + enclosing: enclosing; + enclosingEscape: enclosingEscape; + enoding: encoding; + } +} diff --git a/libs/language-server/src/stdlib/CSVFileInterpreter.jv b/libs/language-server/src/stdlib/CSVFileInterpreter.jv new file mode 100644 index 00000000..4bedc3ef --- /dev/null +++ b/libs/language-server/src/stdlib/CSVFileInterpreter.jv @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** +* A CSVFileInterpreter interprets a file as CSV +*/ +composite blocktype CSVFileInterpreter { + property delimiter oftype text: ','; + property enclosing oftype text: ''; + property enclosingEscape oftype text: ''; + property enoding oftype text: 'utf-8'; + + input inputPort oftype File; + output outputPort oftype Sheet; + + inputPort + -> FileTextInterpreter + -> FileCSVInterpreter + -> outputPort; + + block FileTextInterpreter oftype TextFileInterpreter { + encoding: enoding; + } + + block FileCSVInterpreter oftype CSVInterpreter { + delimiter: delimiter; + enclosing: enclosing; + enclosingEscape: enclosingEscape; + } +} diff --git a/libs/language-server/src/stdlib/builtin-block-types/ArchiveInterpreter.jv b/libs/language-server/src/stdlib/builtin-block-types/ArchiveInterpreter.jv new file mode 100644 index 00000000..d490c5a7 --- /dev/null +++ b/libs/language-server/src/stdlib/builtin-block-types/ArchiveInterpreter.jv @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** +* Interprets a `File` as an archive file and converts it to a `FileSystem`. The archive file root is considered the root of the `FileSystem`. +* +* @example Interprets a `File` as a ZIP-archive and creates a `FileSystem` of its extracted contents. +* block ZipArchiveInterpreter oftype ArchiveInterpreter { +* archiveType: "zip"; +* } +*/ +builtin blocktype ArchiveInterpreter { + input default oftype File; + output default oftype FileSystem; + + /** + * The archive type to be interpreted, e.g., "zip" or "gz". + */ + property archiveType oftype text; +} diff --git a/libs/language-server/src/stdlib/builtin-block-types/CellRangeSelector.jv b/libs/language-server/src/stdlib/builtin-block-types/CellRangeSelector.jv new file mode 100644 index 00000000..87182fa4 --- /dev/null +++ b/libs/language-server/src/stdlib/builtin-block-types/CellRangeSelector.jv @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** +* Selects a subset of a `Sheet` to produce a new `Sheet`. +* +* @example Selects the cells in the given range and produces a new `Sheet` containing only the selected cells. +* block CarsCoreDataSelector oftype CellRangeSelector { +* select: range A1:E*; +* } +*/ +builtin blocktype CellRangeSelector { + input default oftype Sheet; + output default oftype Sheet; + + /** + * The cell range to select. + */ + property select oftype CellRange; +} \ No newline at end of file diff --git a/libs/language-server/src/stdlib/builtin-block-types/CellWriter.jv b/libs/language-server/src/stdlib/builtin-block-types/CellWriter.jv new file mode 100644 index 00000000..6ff64a4e --- /dev/null +++ b/libs/language-server/src/stdlib/builtin-block-types/CellWriter.jv @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** +* Writes textual values into cells of a `Sheet`. The number of text values needs to match the number of cells to write into. +* +* @example Write the value "Name" into cell `A1`. +* block NameHeaderWriter oftype CellWriter { +* at: cell A1; +* write: ["Name"]; +* } +* +* @example Write the values "Name", "Age" into cells `A1` and `A2`. +* block HeaderSequenceWriter oftype CellWriter { +* at: range A1:A2; +* write: ["Name", "Age"]; +* } +*/ +builtin blocktype CellWriter { + input default oftype Sheet; + output default oftype Sheet; + + /** + * The values to write. + */ + property write oftype Collection<text>; + + /** + * The cells to write into. + */ + property at oftype CellRange; +} \ No newline at end of file diff --git a/libs/language-server/src/stdlib/builtin-block-types/ColumnDeleter.jv b/libs/language-server/src/stdlib/builtin-block-types/ColumnDeleter.jv new file mode 100644 index 00000000..1c3f18ec --- /dev/null +++ b/libs/language-server/src/stdlib/builtin-block-types/ColumnDeleter.jv @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** +* Deletes columns from a `Sheet`. Column IDs of subsequent columns will be shifted accordingly, so there will be no gaps. +* +* @example Deletes column B (i.e. the second column). +* block MpgColumnDeleter oftype ColumnDeleter { +* delete: [column B]; +* } +*/ +builtin blocktype ColumnDeleter { + input default oftype Sheet; + output default oftype Sheet; + + /** + * The columns to delete. Has to be a full column. + */ + property delete oftype Collection<CellRange>; +} \ No newline at end of file diff --git a/libs/language-server/src/stdlib/builtin-block-types/CsvInterpreter.jv b/libs/language-server/src/stdlib/builtin-block-types/CsvInterpreter.jv new file mode 100644 index 00000000..fb654e4c --- /dev/null +++ b/libs/language-server/src/stdlib/builtin-block-types/CsvInterpreter.jv @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** +* Interprets an input file as a csv-file containing string-values delimited by `delimiter` and outputs a `Sheet`. +* +* @example Interprets an input file as a csv-file containing string-values delimited by `;` and outputs `Sheet`. +* block AgencyCSVInterpreter oftype CSVInterpreter { +* delimiter: ";"; +* } +*/ +builtin blocktype CSVInterpreter { + input default oftype TextFile; + output default oftype Sheet; + + /** + * The delimiter for values in the CSV file. + */ + property delimiter oftype text: ','; + + /** + * The enclosing character that may be used for values in the CSV file. + */ + property enclosing oftype text: ''; + + /** + * The character to escape enclosing characters in values. + */ + property enclosingEscape oftype text: ''; +} \ No newline at end of file diff --git a/libs/language-server/src/stdlib/builtin-block-types/FilePicker.jv b/libs/language-server/src/stdlib/builtin-block-types/FilePicker.jv new file mode 100644 index 00000000..0b0cb809 --- /dev/null +++ b/libs/language-server/src/stdlib/builtin-block-types/FilePicker.jv @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** +* Selects one `File` from a `FileSystem` based on its relative path to the root of the `FileSystem`. If no file matches the relative path, no output is created and the execution of the pipeline is aborted. +* +* @example Tries to pick the file `agency.txt` from the root of the provided `FileSystem`. If `agency.txt` exists it is passed on as `File`, if it does not exist the execution of the pipeline is aborted. +* block AgencyFilePicker oftype FilePicker { +* path: "./agency.txt"; +* } +*/ +builtin blocktype FilePicker { + input default oftype FileSystem; + output default oftype File; + + /** + * The path of the file to select, relative to the root of the provided `FileSystem`. + */ + property path oftype text; +} \ No newline at end of file diff --git a/libs/language-server/src/stdlib/builtin-block-types/FileToTableInterpreter.jv b/libs/language-server/src/stdlib/builtin-block-types/FileToTableInterpreter.jv new file mode 100644 index 00000000..8b96f0d7 --- /dev/null +++ b/libs/language-server/src/stdlib/builtin-block-types/FileToTableInterpreter.jv @@ -0,0 +1,65 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** +* Interprets a `Sheet` as a `Table`. In case a header row is present in the sheet, its names can be matched with the provided column names. Otherwise, the provided column names are assigned in order. +* +* @example Interprets a `Sheet` about cars with a topmost header row and interprets it as a `Table` by assigning a primitive value type to each column. The column names are matched to the header, so the order of the type assignments does not matter. +* block CarsTableInterpreter oftype TableInterpreter { +* header: true; +* columns: [ +* "name" oftype text, +* "mpg" oftype decimal, +* "cyl" oftype integer, +* ]; +* } +* +* @example Interprets a `Sheet` about cars without a topmost header row and interprets it as a `Table` by sequentially assigning a name and a primitive value type to each column of the sheet. Note that the order of columns matters here. The first column (column `A`) will be named "name", the second column (column `B`) will be named "mpg" etc. +* block CarsTableInterpreter oftype TableInterpreter { +* header: false; +* columns: [ +* "name" oftype text, +* "mpg" oftype decimal, +* "cyl" oftype integer, +* ]; +* } +*/ +builtin blocktype FileToTableInterpreter { + input default oftype File; + output default oftype Table; + + + + /** + * The encoding used for decoding the file contents. + */ + property encoding oftype text: 'utf-8'; + + + + /** + * The delimiter for values in the CSV file. + */ + property delimiter oftype text: ','; + + /** + * The enclosing character that may be used for values in the CSV file. + */ + property enclosing oftype text: ''; + + /** + * The character to escape enclosing characters in values. + */ + property enclosingEscape oftype text: ''; + + /** + * Whether the first row should be interpreted as header row. + */ + property header oftype boolean: true; + + /** + * Collection of value type assignments. Uses column names (potentially matched with the header or by sequence depending on the `header` property) to assign a primitive value type to each column. + */ + property columns oftype Collection<ValuetypeAssignment>; +} diff --git a/libs/language-server/src/stdlib/builtin-block-types/GtfsRtInterpreter.jv b/libs/language-server/src/stdlib/builtin-block-types/GtfsRtInterpreter.jv new file mode 100644 index 00000000..9caf3901 --- /dev/null +++ b/libs/language-server/src/stdlib/builtin-block-types/GtfsRtInterpreter.jv @@ -0,0 +1,68 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** +* Interprets an protobuf file (binary) of type `File` by decoding the file according to `gtfs-realtime.proto`. Outputs the extracted entity defined by `entity` as a `Sheet` +* +* @example A file is interpretet as an GTFS-RT file, which contains TripUpdate. +* block GtfsRTTripUpdateInterpreter oftype GtfsRTInterpreter{ +* entity: "trip_update"; +* } +*/ +builtin blocktype GtfsRTInterpreter { + input default oftype File; + output default oftype Sheet; + + /** + * Entity to process from GTFS-RT-feed (`trip_update`, `alert` or `vehicle`). + * + * We currently support following Output-Sheets, each are an equivalent to the flattened Element Index defined in <https://developers.google.com/transit/gtfs-realtime/reference#element-index> (just required fields are included): + * + * Entity TripUpdate: + * ``` + * [ + * 'header.gtfs_realtime_version', + * 'header.timestamp', + * 'header.incrementality', + * 'entity.id', + * 'entity.trip_update.trip.trip_id', + * 'entity.trip_update.trip.route_id', + * 'entity.trip_update.stop_time_update.stop_sequence', + * 'entity.trip_update.stop_time_update.stop_id', + * 'entity.trip_update.stop_time_update.arrival.time', + * 'entity.trip_update.stop_time_update.departure.time', + * ]; + * + * ``` + * Entity VehiclePosition: + * ``` + * [ + * 'header.gtfs_realtime_version', + * 'header.timestamp', + * 'header.incrementality', + * 'entity.id', + * 'entity.vehicle_position.vehicle_descriptor.id', + * 'entity.vehicle_position.trip.trip_id', + * 'entity.vehicle_position.trip.route_id', + * 'entity.vehicle_position.position.latitude', + * 'entity.vehicle_position.position.longitude', + * 'entity.vehicle_position.timestamp', + * ]; + * ``` + * + * Entity Alert: + * ``` + * [ + * 'header.gtfs_realtime_version', + * 'header.timestamp', + * 'header.incrementality', + * 'entity.id', + * 'entity.alert.informed_entity.route_id', + * 'entity.alert.header_text', + * 'entity.alert.description_text', + * ]; + * ``` + */ + property entity oftype text; +} diff --git a/libs/language-server/src/stdlib/builtin-block-types/HttpExtractor.jv b/libs/language-server/src/stdlib/builtin-block-types/HttpExtractor.jv new file mode 100644 index 00000000..014328c9 --- /dev/null +++ b/libs/language-server/src/stdlib/builtin-block-types/HttpExtractor.jv @@ -0,0 +1,41 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** +* Extracts a `File` from the web. +* +* @example Fetches a file from the given URL. +* block CarsFileExtractor oftype HttpExtractor { +* url: "tinyurl.com/4ub9spwz"; +* } +*/ +builtin blocktype HttpExtractor { + input default oftype None; + output default oftype File; + + /** + * The URL to the file in the web to extract. + */ + property url oftype text; + + /** + * Configures how many retries should be executed after a failure fetching the data. + */ + property retries oftype integer: 0; + + /** + * Configures the wait time in milliseconds before executing a retry. + */ + property retryBackoffMilliseconds oftype integer: 1000; + + /** + * Configures the wait strategy before executing a retry. Can have values "exponential" or "linear". + */ + property retryBackoffStrategy oftype text: "exponential"; + + /** + * Indicates, whether to follow redirects on get requests. If `false`, redirects are not followed. Default `true` + */ + property followRedirects oftype boolean: true; +} diff --git a/libs/language-server/src/stdlib/builtin-block-types/LocalFileExtractor.jv b/libs/language-server/src/stdlib/builtin-block-types/LocalFileExtractor.jv new file mode 100644 index 00000000..a4f7a3c5 --- /dev/null +++ b/libs/language-server/src/stdlib/builtin-block-types/LocalFileExtractor.jv @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** +* Extracts a `File` from the local file system. +* +* @example Extracts a file from the given path on the local file system. +* block CarsFileExtractor oftype LocalFileExtractor { +* filePath: "cars.csv"; +* } +*/ +builtin blocktype LocalFileExtractor { + input default oftype None; + output default oftype File; + + /** + * The path to the file in the local file system to extract. Path can not traverse up the directory tree. + */ + property filePath oftype text; + +} \ No newline at end of file diff --git a/libs/language-server/src/stdlib/builtin-block-types/PostgresLoader.jv b/libs/language-server/src/stdlib/builtin-block-types/PostgresLoader.jv new file mode 100644 index 00000000..2b2a824a --- /dev/null +++ b/libs/language-server/src/stdlib/builtin-block-types/PostgresLoader.jv @@ -0,0 +1,51 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** +* Loads a `Table` into a PostgreSQL database sink. +* +* @example A local Postgres instance is filled with table data about cars. +* block CarsLoader oftype PostgresLoader { +* host: "localhost"; +* port: 5432; +* username: "postgres"; +* password: "postgres"; +* database: "CarsDB"; +* table: "Cars"; +* } +*/ +builtin blocktype PostgresLoader { + input default oftype Table; + output default oftype None; + + /** + * The hostname or IP address of the Postgres database. + */ + property host oftype text; + + /** + * The port of the Postgres database. + */ + property port oftype integer; + + /** + * The username to login to the Postgres database. + */ + property username oftype text; + + /** + * The password to login to the Postgres database. + */ + property password oftype text; + + /** + * The database to use. + */ + property database oftype text; + + /** + * The name of the table to write into. + */ + property table oftype text; +} \ No newline at end of file diff --git a/libs/language-server/src/stdlib/builtin-block-types/RowDeleter.jv b/libs/language-server/src/stdlib/builtin-block-types/RowDeleter.jv new file mode 100644 index 00000000..9835a232 --- /dev/null +++ b/libs/language-server/src/stdlib/builtin-block-types/RowDeleter.jv @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** +* Deletes one or more rows from a `Sheet`. Row IDs of subsequent rows will be shifted accordingly, so there will be no gaps. +* +* @example Deletes row 2 (i.e. the second row). +* block SecondRowDeleter oftype RowDeleter { +* delete: [row 2]; +* } +*/ +builtin blocktype RowDeleter { + input default oftype Sheet; + output default oftype Sheet; + + /** + * The rows to delete. + */ + property delete oftype Collection<CellRange>; +} \ No newline at end of file diff --git a/libs/language-server/src/stdlib/builtin-block-types/SheetPicker.jv b/libs/language-server/src/stdlib/builtin-block-types/SheetPicker.jv new file mode 100644 index 00000000..e9591c88 --- /dev/null +++ b/libs/language-server/src/stdlib/builtin-block-types/SheetPicker.jv @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** +* Selects one `Sheet` from a `Workbook` based on its `sheetName`. If no sheet matches the name, no output is created and the execution of the pipeline is aborted. +* +* @example Tries to pick the sheet `AgencyNames` from the provided `Workbook`. If `AgencyNames` exists it is passed on as `Sheet`, if it does not exist the execution of the pipeline is aborted. +* block AgencySheetPicker oftype SheetPicker { +* sheetName: "AgencyNames"; +* } +*/ +builtin blocktype SheetPicker { + input default oftype Workbook; + output default oftype Sheet; + + /** + * The name of the sheet to select. + */ + property sheetName oftype text; +} \ No newline at end of file diff --git a/libs/language-server/src/stdlib/builtin-block-types/SqliteLoader.jv b/libs/language-server/src/stdlib/builtin-block-types/SqliteLoader.jv new file mode 100644 index 00000000..3a69197e --- /dev/null +++ b/libs/language-server/src/stdlib/builtin-block-types/SqliteLoader.jv @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** +* Loads a `Table` into a SQLite database sink. +* +* @example A SQLite file `cars.db` is created in the working directory. Incoming data is written to the table `cars`. +* block CarsLoader oftype SQLiteLoader { +* table: "cars"; +* file: "./cars.db"; +* } +*/ +builtin blocktype SQLiteLoader { + input default oftype Table; + output default oftype None; + + /** + * The name of the table to write into. + */ + property table oftype text; + + /** + * The path to a SQLite file that will be created if it does not exist. Usual file extensions are `.sqlite` and `.db`. + */ + property file oftype text; + + /** + * Indicates, whether to drop the table before loading data into it. If `false`, data is appended to the table instead of dropping it. + */ + property dropTable oftype boolean: true; +} \ No newline at end of file diff --git a/libs/language-server/src/stdlib/builtin-block-types/TableInterpreter.jv b/libs/language-server/src/stdlib/builtin-block-types/TableInterpreter.jv new file mode 100644 index 00000000..b2e66012 --- /dev/null +++ b/libs/language-server/src/stdlib/builtin-block-types/TableInterpreter.jv @@ -0,0 +1,41 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** +* Interprets a `Sheet` as a `Table`. In case a header row is present in the sheet, its names can be matched with the provided column names. Otherwise, the provided column names are assigned in order. +* +* @example Interprets a `Sheet` about cars with a topmost header row and interprets it as a `Table` by assigning a primitive value type to each column. The column names are matched to the header, so the order of the type assignments does not matter. +* block CarsTableInterpreter oftype TableInterpreter { +* header: true; +* columns: [ +* "name" oftype text, +* "mpg" oftype decimal, +* "cyl" oftype integer, +* ]; +* } +* +* @example Interprets a `Sheet` about cars without a topmost header row and interprets it as a `Table` by sequentially assigning a name and a primitive value type to each column of the sheet. Note that the order of columns matters here. The first column (column `A`) will be named "name", the second column (column `B`) will be named "mpg" etc. +* block CarsTableInterpreter oftype TableInterpreter { +* header: false; +* columns: [ +* "name" oftype text, +* "mpg" oftype decimal, +* "cyl" oftype integer, +* ]; +* } +*/ +builtin blocktype TableInterpreter { + input default oftype Sheet; + output default oftype Table; + + /** + * Whether the first row should be interpreted as header row. + */ + property header oftype boolean: true; + + /** + * Collection of value type assignments. Uses column names (potentially matched with the header or by sequence depending on the `header` property) to assign a primitive value type to each column. + */ + property columns oftype Collection<ValuetypeAssignment>; +} \ No newline at end of file diff --git a/libs/language-server/src/stdlib/builtin-block-types/TableTransformer.jv b/libs/language-server/src/stdlib/builtin-block-types/TableTransformer.jv new file mode 100644 index 00000000..6a022ffb --- /dev/null +++ b/libs/language-server/src/stdlib/builtin-block-types/TableTransformer.jv @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** +* Applies a transform on each value of a column. The input port type of the used transform has to match the type of the input column. +* +* @example Given a column "temperature" with temperature values in Celsius, it overwrites the column with computed values in Fahrenheit by using the `CelsiusToFahrenheit` transform. The transform itself is defined elsewhere in the model. +* +* transform CelsiusToFahrenheit { +* from Celsius oftype decimal; +* to Fahrenheit oftype decimal; +* +* Fahrenheit: (Celsius * 9/5) + 32; +* } +* +* block CelsiusToFahrenheitTransformer oftype TableTransformer { +* inputColumns: ['temperature']; +* outputColumn: 'temperature'; +* use: CelsiusToFahrenheit; +* } +* +* @example Given a column "temperatureCelsius" with temperature values in Celsius, it adds a new column "temperatureFahrenheit" with computed values in Fahrenheit by using the `CelsiusToFahrenheit` transform. The transform itself is defined elsewhere in the model. +* +* transform CelsiusToFahrenheit { +* from Celsius oftype decimal; +* to Fahrenheit oftype decimal; +* +* Fahrenheit: (Celsius * 9/5) + 32; +* } +* +* block CelsiusToFahrenheitTransformer oftype TableTransformer { +* inputColumns: ['temperatureCelsius']; +* outputColumn: 'temperatureFahrenheit'; +* use: CelsiusToFahrenheit; +* } +*/ +builtin blocktype TableTransformer { + input default oftype Table; + output default oftype Table; + + /** + * The names of the input columns. The columns have to be present in the table and match with the transform's input port types. + */ + property inputColumns oftype Collection<text>; + + /** + * The name of the output column. Overwrites the column if it already exists, or otherwise creates a new one. + */ + property outputColumn oftype text; + + /** + * Reference to the transform that is applied to the column. + */ + property use oftype Transform; +} \ No newline at end of file diff --git a/libs/language-server/src/stdlib/builtin-block-types/TextFileInterpreter.jv b/libs/language-server/src/stdlib/builtin-block-types/TextFileInterpreter.jv new file mode 100644 index 00000000..32e1a46c --- /dev/null +++ b/libs/language-server/src/stdlib/builtin-block-types/TextFileInterpreter.jv @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** +* Interprets a `File` as a `TextFile`. +*/ +builtin blocktype TextFileInterpreter { + input default oftype File; + output default oftype TextFile; + + /** + * The encoding used for decoding the file contents. + */ + property encoding oftype text: 'utf-8'; + + /** + * The regex for identifying line breaks. + */ + property lineBreak oftype Regex: /\r?\n/; +} diff --git a/libs/language-server/src/stdlib/builtin-block-types/TextLineDeleter.jv b/libs/language-server/src/stdlib/builtin-block-types/TextLineDeleter.jv new file mode 100644 index 00000000..22699622 --- /dev/null +++ b/libs/language-server/src/stdlib/builtin-block-types/TextLineDeleter.jv @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** +* Deletes individual lines from a `TextFile`. +*/ +builtin blocktype TextLineDeleter { + input default oftype TextFile; + output default oftype TextFile; + + /** + * The line numbers to delete. + */ + property lines oftype Collection<integer>; +} diff --git a/libs/language-server/src/stdlib/builtin-block-types/TextRangeSelector.jv b/libs/language-server/src/stdlib/builtin-block-types/TextRangeSelector.jv new file mode 100644 index 00000000..df0cda4a --- /dev/null +++ b/libs/language-server/src/stdlib/builtin-block-types/TextRangeSelector.jv @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** +* Selects a range of lines from a `TextFile`. +*/ +builtin blocktype TextRangeSelector { + input default oftype TextFile; + output default oftype TextFile; + + /** + * Inclusive beginning line number for the selection. + */ + property lineFrom oftype integer: 1; + + /** + * Inclusive ending line number for the selection. + * The default value is the biggest usable integer. + */ + property lineTo oftype integer: 9007199254740991; +} \ No newline at end of file diff --git a/libs/language-server/src/stdlib/builtin-block-types/XlsInterpreter.jv b/libs/language-server/src/stdlib/builtin-block-types/XlsInterpreter.jv new file mode 100644 index 00000000..40c3fcfc --- /dev/null +++ b/libs/language-server/src/stdlib/builtin-block-types/XlsInterpreter.jv @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** +* Interprets an input file as a XLSX-file and outputs a `Workbook` containing `Sheet`s. +* +* @example Interprets an input file as a XLSX-file and outputs a `Workbook` containing `Sheet`s. +* block AgencyXLSXInterpreter oftype XLSXInterpreter { } +*/ +builtin blocktype XLSXInterpreter { + input default oftype File; + output default oftype Workbook; +} \ No newline at end of file diff --git a/libs/language-server/src/stdlib/builtin-constrainttypes/AllowlistConstraint.jv b/libs/language-server/src/stdlib/builtin-constrainttypes/AllowlistConstraint.jv new file mode 100644 index 00000000..a6c25b91 --- /dev/null +++ b/libs/language-server/src/stdlib/builtin-constrainttypes/AllowlistConstraint.jv @@ -0,0 +1,15 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** +* Limits the values to a defined a set of allowed values. Only values in the list are valid. +* +* @example Only allows the common abbreviations for millisecond, second, minute, etc.. +* constraint TimeUnitString oftype AllowlistConstraint { +* allowlist: ["ms", "s", "min", "h", "d", "m", "y"]; +* } +*/ +builtin constrainttype AllowlistConstraint on text { + property allowlist oftype Collection<text> ; +} \ No newline at end of file diff --git a/libs/language-server/src/stdlib/builtin-constrainttypes/DenylistConstraint.jv b/libs/language-server/src/stdlib/builtin-constrainttypes/DenylistConstraint.jv new file mode 100644 index 00000000..fc554b2f --- /dev/null +++ b/libs/language-server/src/stdlib/builtin-constrainttypes/DenylistConstraint.jv @@ -0,0 +1,15 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** +* Defines a set of forbidden values. All values in the list are considered invalid. +* +* @example Denies all primary colors. +* constraint NoPrimaryColors oftype DenylistConstraint { +* denylist: ["red", "blue", "yellow"]; +* } +*/ +builtin constrainttype DenylistConstraint on text { + property denylist oftype Collection<text> ; +} \ No newline at end of file diff --git a/libs/language-server/src/stdlib/builtin-constrainttypes/LengthConstraint.jv b/libs/language-server/src/stdlib/builtin-constrainttypes/LengthConstraint.jv new file mode 100644 index 00000000..a51f69aa --- /dev/null +++ b/libs/language-server/src/stdlib/builtin-constrainttypes/LengthConstraint.jv @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** +* Limits the length of a string with an upper and/or lower boundary. +* Only values with a length within the given range are valid. +* +* @example A text constraint with 0 to 20 characters. +* constraint ShortAnswerConstraint oftype LengthConstraint { +* minLength: 0; +* maxLength: 20; +* } +*/ +builtin constrainttype LengthConstraint on text { + /** + * Inclusive minimum of the valid text length. + */ + property minLength oftype integer : 0; + + /** + * Inclusive maximum of the valid text length. + * The default value is the biggest usable integer. + */ + property maxLength oftype integer : 9007199254740991; +} \ No newline at end of file diff --git a/libs/language-server/src/stdlib/builtin-constrainttypes/RangeConstraint.jv b/libs/language-server/src/stdlib/builtin-constrainttypes/RangeConstraint.jv new file mode 100644 index 00000000..fa74774a --- /dev/null +++ b/libs/language-server/src/stdlib/builtin-constrainttypes/RangeConstraint.jv @@ -0,0 +1,45 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** +* Limits the range of a number value with an upper and/or lower boundary which can be inclusive or exclusive. Only values within the given range are considered valid. +* +* @example A scale between (and including) 1 and 100. +* constraint HundredScale oftype RangeConstraint { +* lowerBound: 1; +* upperBound: 100; +* } +* +* @example A scale for numbers strictly larger than 1 and less or equal to 100. +* constraint HundredScale oftype RangeConstraint { +* lowerBound: 1; +* lowerBoundInclusive: false; +* upperBound: 100; +* } +*/ +builtin constrainttype RangeConstraint on decimal { + /** + * Lower bound for the valid value range. + * @see lowerBoundInclusive whether inclusive or exclusive. + * The default value is the smallest usable integer. + */ + property lowerBound oftype decimal : -9007199254740991; + + /** + * Boolean flag whether @see lowerBound is inclusive or exclusive. + */ + property lowerBoundInclusive oftype boolean : true; + + /** + * Upper bound for the valid value range. + * @see upperBoundInclusive whether inclusive or exclusive. + * The default value is the biggest usable integer. + */ + property upperBound oftype decimal : 9007199254740991; + + /** + * Boolean flag whether @see upperBound is inclusive or exclusive. + */ + property upperBoundInclusive oftype boolean : true; +} \ No newline at end of file diff --git a/libs/language-server/src/stdlib/builtin-constrainttypes/RegexConstraint.jv b/libs/language-server/src/stdlib/builtin-constrainttypes/RegexConstraint.jv new file mode 100644 index 00000000..17ef4270 --- /dev/null +++ b/libs/language-server/src/stdlib/builtin-constrainttypes/RegexConstraint.jv @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** +* Limits the values complying with a regex. +* Only values that comply with the regex are considered valid. +* +* @example Text that complies with the IPv4 address format. +* constraint IPv4Format oftype RegexConstraint { +* regex: /^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$/; +* } +*/ +builtin constrainttype RegexConstraint on text { + property regex oftype Regex ; +} \ No newline at end of file diff --git a/libs/language-server/src/stdlib/domain/mobility/GTFSAgencyInterpreter.jv b/libs/language-server/src/stdlib/domain/mobility/GTFSAgencyInterpreter.jv new file mode 100644 index 00000000..61edadc7 --- /dev/null +++ b/libs/language-server/src/stdlib/domain/mobility/GTFSAgencyInterpreter.jv @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** +* A GTFSAgencyInterpreter interprets a agency.txt file from an extracted ZIP file according to the GTFS standard +* See https://gtfs.org/schedule/reference/#agencytxt +*/ +composite blocktype GTFSAgencyInterpreter { + + input inputPort oftype FileSystem; + output outputPort oftype Table; + + inputPort + -> AgencyFilePicker + -> AgencyTextFileInterpreter + -> AgencyCSVInterpreter + -> AgencyTableInterpreter + -> outputPort; + + block AgencyFilePicker oftype FilePicker { + path: "/agency.txt"; + } + + block AgencyTextFileInterpreter oftype TextFileInterpreter { } + block AgencyCSVInterpreter oftype CSVInterpreter { } + + block AgencyTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "agency_id" oftype text, // Conditional columns are considered required for now + "agency_name" oftype text, + "agency_url" oftype GTFSUrl, + "agency_timezone" oftype text + ]; + } +} diff --git a/libs/language-server/src/stdlib/domain/mobility/GTFSCalendarDatesInterpreter.jv b/libs/language-server/src/stdlib/domain/mobility/GTFSCalendarDatesInterpreter.jv new file mode 100644 index 00000000..b08b0f34 --- /dev/null +++ b/libs/language-server/src/stdlib/domain/mobility/GTFSCalendarDatesInterpreter.jv @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** +* A GTFSCalendarDatesInterpreter interprets a calendar_dates.txt file from an extracted ZIP file according to the GTFS standard +* See https://gtfs.org/schedule/reference/#calendar_datestxt +*/ +composite blocktype GTFSCalendarDatesInterpreter { + + input inputPort oftype FileSystem; + output outputPort oftype Table; + + inputPort + -> CalendarDatesFilePicker + -> CalendarDatesTextFileInterpreter + -> CalendarDatesCSVInterpreter + -> CalendarDatesTableInterpreter + -> outputPort; + + block CalendarDatesFilePicker oftype FilePicker { + path: "/calendar_dates.txt"; + } + + block CalendarDatesTextFileInterpreter oftype TextFileInterpreter { } + block CalendarDatesCSVInterpreter oftype CSVInterpreter { } + + block CalendarDatesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "service_id" oftype text, + "date" oftype GTFSDate, + "exception_type" oftype GTFSEnumOneOrTwo // 1 - Service has been added for the specified date + // 2 - Service has been removed for the specified date. + ]; + } +} diff --git a/libs/language-server/src/stdlib/domain/mobility/GTFSCalendarInterpreter.jv b/libs/language-server/src/stdlib/domain/mobility/GTFSCalendarInterpreter.jv new file mode 100644 index 00000000..be8b32a7 --- /dev/null +++ b/libs/language-server/src/stdlib/domain/mobility/GTFSCalendarInterpreter.jv @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** +* A GTFSCalendarInterpreter interprets a calendar.txt file from an extracted ZIP file according to the GTFS standard +* See https://gtfs.org/schedule/reference/#calendartxt +*/ +composite blocktype GTFSCalendarInterpreter { + + input inputPort oftype FileSystem; + output outputPort oftype Table; + + inputPort + -> CalendarFilePicker + -> CalendarTextFileInterpreter + -> CalendarCSVInterpreter + -> CalendarTableInterpreter + -> outputPort; + + block CalendarFilePicker oftype FilePicker { + path: "/calendar.txt"; + } + + block CalendarTextFileInterpreter oftype TextFileInterpreter { } + block CalendarCSVInterpreter oftype CSVInterpreter { } + + block CalendarTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "service_id" oftype text, + "monday" oftype GTFSEnumTwo, // 1 - Service is available for all Mondays in the date range. + // 0 - Service is not available for Mondays in the date range. + "tuesday" oftype GTFSEnumTwo, + "wednesday" oftype GTFSEnumTwo, + "thursday" oftype GTFSEnumTwo, + "friday" oftype GTFSEnumTwo, + "saturday" oftype GTFSEnumTwo, + "sunday" oftype GTFSEnumTwo, + "start_date" oftype GTFSDate, + "end_date" oftype GTFSDate + ]; + } +} diff --git a/libs/language-server/src/stdlib/domain/mobility/GTFSExtractor.jv b/libs/language-server/src/stdlib/domain/mobility/GTFSExtractor.jv new file mode 100644 index 00000000..d5699c36 --- /dev/null +++ b/libs/language-server/src/stdlib/domain/mobility/GTFSExtractor.jv @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** +* A GTFSExtractor extracts a file from a given URL, interprets it as ZIP and extracts it +*/ +composite blocktype GTFSExtractor { + property url oftype text; + + input inputPort oftype None; + output outputPort oftype FileSystem; + + inputPort + -> FileExtractor + -> ZipArchiveInterpreter + -> outputPort; + + block FileExtractor oftype HttpExtractor { url: url; } + + block ZipArchiveInterpreter oftype ArchiveInterpreter { archiveType: "zip"; } +} diff --git a/libs/language-server/src/stdlib/domain/mobility/GTFSFareAttributesInterpreter.jv b/libs/language-server/src/stdlib/domain/mobility/GTFSFareAttributesInterpreter.jv new file mode 100644 index 00000000..40c2cb25 --- /dev/null +++ b/libs/language-server/src/stdlib/domain/mobility/GTFSFareAttributesInterpreter.jv @@ -0,0 +1,40 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** +* A GTFSFareAttributesInterpreter interprets a fare_attributes.txt file from an extracted ZIP file according to the GTFS standard +* See https://gtfs.org/schedule/reference/#fare_attributestxt +*/ +composite blocktype GTFSFareAttributesInterpreter { + + input inputPort oftype FileSystem; + output outputPort oftype Table; + + inputPort + -> FareAttributesFilePicker + -> FareAttributesTextFileInterpreter + -> FareAttributesCSVInterpreter + -> FareAttributesTableInterpreter + -> outputPort; + + block FareAttributesFilePicker oftype FilePicker { + path: "/fare_attributes.txt"; + } + + block FareAttributesTextFileInterpreter oftype TextFileInterpreter { } + block FareAttributesCSVInterpreter oftype CSVInterpreter { } + + block FareAttributesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "fare_id" oftype text, + "price" oftype GTFSNonNegativeDecimal, + "currency_type" oftype GTFSCurrency, + "payment_method" oftype GTFSEnumTwo, // 0 - Fare is paid on board. + // 1 - Fare must be paid before boarding. + "transfers" oftype text, // Is required but can be empty (?!) so has to be modelled as text... + "transfer_duration" oftype text + ]; + } +} diff --git a/libs/language-server/src/stdlib/domain/mobility/GTFSFareRulesInterpreter.jv b/libs/language-server/src/stdlib/domain/mobility/GTFSFareRulesInterpreter.jv new file mode 100644 index 00000000..0cd5f995 --- /dev/null +++ b/libs/language-server/src/stdlib/domain/mobility/GTFSFareRulesInterpreter.jv @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** +* A GTFSFareRulesInterpreter interprets a fare_rules.txt file from an extracted ZIP file according to the GTFS standard +* See https://gtfs.org/schedule/reference/#fare_rulestxt +*/ +composite blocktype GTFSFareRulesInterpreter { + + input inputPort oftype FileSystem; + output outputPort oftype Table; + + inputPort + -> FareRulesFilePicker + -> FareRulesTextFileInterpreter + -> FareRulesCSVInterpreter + -> FareRulesTableInterpreter + -> outputPort; + + block FareRulesFilePicker oftype FilePicker { + path: "/fare_rules.txt"; + } + + block FareRulesTextFileInterpreter oftype TextFileInterpreter { } + block FareRulesCSVInterpreter oftype CSVInterpreter { } + + block FareRulesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "fare_id" oftype text, + "route_id" oftype text, + "origin_id" oftype text, + "destination_id" oftype text, + "contains_id" oftype text + ]; + } +} diff --git a/libs/language-server/src/stdlib/domain/mobility/GTFSFrequenciesInterpreter.jv b/libs/language-server/src/stdlib/domain/mobility/GTFSFrequenciesInterpreter.jv new file mode 100644 index 00000000..5d812090 --- /dev/null +++ b/libs/language-server/src/stdlib/domain/mobility/GTFSFrequenciesInterpreter.jv @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** +* A GTFSFrequenciesInterpreter interprets a frequencies.txt file from an extracted ZIP file according to the GTFS standard +* See https://gtfs.org/schedule/reference/#frequenciestxt +*/ +composite blocktype GTFSFrequenciesInterpreter { + + input inputPort oftype FileSystem; + output outputPort oftype Table; + + inputPort + -> FrequenciesFilePicker + -> FrequenciesTextFileInterpreter + -> FrequenciesCSVInterpreter + -> FrequenciesTableInterpreter + -> outputPort; + + block FrequenciesFilePicker oftype FilePicker { + path: "/frequencies.txt"; + } + + block FrequenciesTextFileInterpreter oftype TextFileInterpreter { } + block FrequenciesCSVInterpreter oftype CSVInterpreter { } + + block FrequenciesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "trip_id" oftype text, + "start_time" oftype GTFSTime, + "end_time" oftype GTFSTime, + "headway_secs" oftype GTFSNonNegativeInteger + ]; + } +} diff --git a/libs/language-server/src/stdlib/domain/mobility/GTFSRoutesInterpreter.jv b/libs/language-server/src/stdlib/domain/mobility/GTFSRoutesInterpreter.jv new file mode 100644 index 00000000..45a8e9ca --- /dev/null +++ b/libs/language-server/src/stdlib/domain/mobility/GTFSRoutesInterpreter.jv @@ -0,0 +1,42 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** +* A GTFSRoutesInterpreter interprets a routes.txt file from an extracted ZIP file according to the GTFS standard +* See https://gtfs.org/schedule/reference/#routestxt +*/ +composite blocktype GTFSRoutesInterpreter { + + input inputPort oftype FileSystem; + output outputPort oftype Table; + + inputPort + -> RoutesFilePicker + -> RoutesTextFileInterpreter + -> RoutesCSVInterpreter + -> RoutesTableInterpreter + -> outputPort; + + block RoutesFilePicker oftype FilePicker { + path: "/routes.txt"; + } + + block RoutesTextFileInterpreter oftype TextFileInterpreter { } + block RoutesCSVInterpreter oftype CSVInterpreter { } + + block RoutesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "route_id" oftype text, + "agency_id" oftype text, + "route_short_name" oftype text, + "route_long_name" oftype text, + "route_desc" oftype text, + "route_type" oftype integer, // Technically is an enum from 0 - 12 + "route_url" oftype text, + "route_color" oftype text, + "route_text_color" oftype text + ]; + } +} diff --git a/libs/language-server/src/stdlib/domain/mobility/GTFSShapesInterpreter.jv b/libs/language-server/src/stdlib/domain/mobility/GTFSShapesInterpreter.jv new file mode 100644 index 00000000..bf830d44 --- /dev/null +++ b/libs/language-server/src/stdlib/domain/mobility/GTFSShapesInterpreter.jv @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** +* A GTFSShapesInterpreter interprets a shapes.txt file from an extracted ZIP file according to the GTFS standard +* See https://gtfs.org/schedule/reference/#shapestxt +*/ +composite blocktype GTFSShapesInterpreter { + + input inputPort oftype FileSystem; + output outputPort oftype Table; + + inputPort + -> ShapesFilePicker + -> ShapesTextFileInterpreter + -> ShapesCSVInterpreter + -> ShapesTableInterpreter + -> outputPort; + + block ShapesFilePicker oftype FilePicker { + path: "/shapes.txt"; + } + + block ShapesTextFileInterpreter oftype TextFileInterpreter { } + block ShapesCSVInterpreter oftype CSVInterpreter { } + + block ShapesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "shape_id" oftype text, + "shape_pt_lat" oftype GTFSLatitude, + "shape_pt_lon" oftype GTFSLongitude, + "shape_pt_sequence" oftype GTFSNonNegativeInteger, + "shape_dist_traveled" oftype text + ]; + } +} diff --git a/libs/language-server/src/stdlib/domain/mobility/GTFSStopTimesInterpreter.jv b/libs/language-server/src/stdlib/domain/mobility/GTFSStopTimesInterpreter.jv new file mode 100644 index 00000000..23862c2b --- /dev/null +++ b/libs/language-server/src/stdlib/domain/mobility/GTFSStopTimesInterpreter.jv @@ -0,0 +1,42 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** +* A GTFSStopTimesInterpreter interprets a stop_times.txt file from an extracted ZIP file according to the GTFS standard +* See https://gtfs.org/schedule/reference/#stop_timestxt +*/ +composite blocktype GTFSStopTimesInterpreter { + + input inputPort oftype FileSystem; + output outputPort oftype Table; + + inputPort + -> StopTimesFilePicker + -> StopTimesTextFileInterpreter + -> StopTimesCSVInterpreter + -> StopTimesTableInterpreter + -> outputPort; + + block StopTimesFilePicker oftype FilePicker { + path: "/stop_times.txt"; + } + + block StopTimesTextFileInterpreter oftype TextFileInterpreter { } + block StopTimesCSVInterpreter oftype CSVInterpreter { } + + block StopTimesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "trip_id" oftype text, + "arrival_time" oftype GTFSTime, + "departure_time" oftype GTFSTime, + "stop_id" oftype text, + "stop_sequence" oftype GTFSNonNegativeInteger, + "stop_headsign" oftype text, + "pickup_type" oftype text, + "drop_off_time" oftype text, + "shape_dist_traveled" oftype text + ]; + } +} diff --git a/libs/language-server/src/stdlib/domain/mobility/GTFSStopsInterpreter.jv b/libs/language-server/src/stdlib/domain/mobility/GTFSStopsInterpreter.jv new file mode 100644 index 00000000..3d634491 --- /dev/null +++ b/libs/language-server/src/stdlib/domain/mobility/GTFSStopsInterpreter.jv @@ -0,0 +1,40 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** +* A GTFSStopsInterpreter interprets a stops.txt file from an extracted ZIP file according to the GTFS standard +* See https://gtfs.org/schedule/reference/#stopstxt +*/ +composite blocktype GTFSStopsInterpreter { + + input inputPort oftype FileSystem; + output outputPort oftype Table; + + inputPort + -> StopsFilePicker + -> StopsTextFileInterpreter + -> StopsCSVInterpreter + -> StopsTableInterpreter + -> outputPort; + + block StopsFilePicker oftype FilePicker { + path: "/stops.txt"; + } + + block StopsTextFileInterpreter oftype TextFileInterpreter { } + block StopsCSVInterpreter oftype CSVInterpreter { } + + block StopsTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "stop_id" oftype text, + "stop_name" oftype text, + "stop_desc" oftype text, + "stop_lat" oftype GTFSLatitude, + "stop_lon" oftype GTFSLongitude, + "zone_id" oftype text, + "stop_url" oftype text + ]; + } +} diff --git a/libs/language-server/src/stdlib/domain/mobility/GTFSTripsInterpreter.jv b/libs/language-server/src/stdlib/domain/mobility/GTFSTripsInterpreter.jv new file mode 100644 index 00000000..163e5dcf --- /dev/null +++ b/libs/language-server/src/stdlib/domain/mobility/GTFSTripsInterpreter.jv @@ -0,0 +1,40 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/** +* A GTFSTripsInterpreter interprets a trips.txt file from an extracted ZIP file according to the GTFS standard +* See https://gtfs.org/schedule/reference/#tripstxt +*/ +composite blocktype GTFSTripsInterpreter { + + input inputPort oftype FileSystem; + output outputPort oftype Table; + + inputPort + -> TripsFilePicker + -> TripsTextFileInterpreter + -> TripsCSVInterpreter + -> TripsTableInterpreter + -> outputPort; + + block TripsFilePicker oftype FilePicker { + path: "/trips.txt"; + } + + block TripsTextFileInterpreter oftype TextFileInterpreter { } + block TripsCSVInterpreter oftype CSVInterpreter { } + + block TripsTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "route_id" oftype text, + "service_id" oftype text, + "trip_id" oftype text, + "trip_headsign" oftype text, + "direction_id" oftype text, + "block_id" oftype text, + "shape_id" oftype text + ]; + } +} diff --git a/libs/language-server/src/stdlib/domain/mobility/GTFSValueTypes.jv b/libs/language-server/src/stdlib/domain/mobility/GTFSValueTypes.jv new file mode 100644 index 00000000..3fd76f4d --- /dev/null +++ b/libs/language-server/src/stdlib/domain/mobility/GTFSValueTypes.jv @@ -0,0 +1,97 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/* + * All field type docs in this file are from: https://gtfs.org/schedule/reference/#field-types + */ + +/* + * Color - A color encoded as a six-digit hexadecimal number. Refer to https://htmlcolorcodes.com to generate a valid value (the leading "#" must not be included). + * Example: FFFFFF for white, 000000 for black or 0039A6 for the A,C,E lines in NYMTA. + */ +constraint GTFSColorConstraint on text: value matches /^[A-F]{6}$/; +valuetype GTFSColor oftype text { + constraints: [GTFSColorConstraint]; +} + +/* + * Date - Service day in the YYYYMMDD format. Since time within a service day may be above 24:00:00, a service day may contain information for the subsequent day(s). + * Example: 20180913 for September 13th, 2018. + */ +constraint DateYYYYMMDD on text: value matches /^[0-9]{4}[0-9]{2}[0-9]{2}$/; +valuetype GTFSDate oftype text { + constraints: [DateYYYYMMDD]; +} + +/* + * Currency code - An ISO 4217 alphabetical currency code. For the list of current currency, refer to https://en.wikipedia.org/wiki/ISO_4217#Active_codes. + * Example: CAD for Canadian dollars, EUR for euros or JPY for Japanese yen. + */ +constraint CurrencyConstraint on text: value matches /^[A-Z]{3}$/; +valuetype GTFSCurrency oftype text { + constraints: [CurrencyConstraint]; +} + +/* + * Time - Time in the HH:MM:SS format (H:MM:SS is also accepted). The time is measured from "noon minus 12h" of the service day (effectively midnight except for days on which daylight savings time changes occur). For times occurring after midnight on the service day, enter the time as a value greater than 24:00:00 in HH:MM:SS. + * Example: 14:30:00 for 2:30PM or 25:35:00 for 1:35AM on the next day. + */ +constraint TimeHHMMSS on text: value matches /^[0-9]{1,2}:{1}[0-9]{2}:{1}[0-9]{2}$/; +valuetype GTFSTime oftype text { + constraints: [TimeHHMMSS]; +} + +// Placeholders +constraint EnumTwo on integer: value in [0, 1]; +constraint EnumOneOrTwo on integer: value in [1, 2]; +constraint EnumThree on integer: value in [0, 1, 2]; + +valuetype GTFSEnumTwo oftype integer { + constraints: [EnumTwo]; +} + +valuetype GTFSEnumOneOrTwo oftype integer { + constraints: [EnumOneOrTwo]; +} + +valuetype GTFSEnumThree oftype integer { + constraints: [EnumThree]; +} + +// Generic value types + +/* + * Latitude - WGS84 latitude in decimal degrees. The value must be greater than or equal to -90.0 and less than or equal to 90.0. + * Example: 41.890169 for the Colosseum in Rome. + */ +constraint Latitude on decimal: value >= -90 and value <= 90; +valuetype GTFSLatitude oftype decimal { + constraints: [Latitude]; +} + +/* + * Longitude - WGS84 longitude in decimal degrees. The value must be greater than or equal to -180.0 and less than or equal to 180.0. + * Example: 12.492269 for the Colosseum in Rome. + */ +constraint Longitude on decimal: value >= -180 and value <= 180; +valuetype GTFSLongitude oftype decimal { + constraints: [Longitude]; +} + +constraint NonNegativeNumber on decimal: value >= 0; +valuetype GTFSNonNegativeDecimal oftype decimal { + constraints: [NonNegativeNumber]; +} + +valuetype GTFSNonNegativeInteger oftype integer { + constraints: [NonNegativeNumber]; +} + +/* + * URL - A fully qualified URL that includes http:// or https://, and any special characters in the URL must be correctly escaped. See the following http://www.w3.org/Addressing/URL/4_URI_Recommentations.html for a description of how to create fully qualified URL values. + */ +constraint URLIncludingSchema on text: value matches /^(http)s?(:\/\/)/; +valuetype GTFSUrl oftype text { + constraints: [URLIncludingSchema]; +} diff --git a/libs/language-server/src/stdlib/percent.jv b/libs/language-server/src/stdlib/percent.jv new file mode 100644 index 00000000..a4ab856e --- /dev/null +++ b/libs/language-server/src/stdlib/percent.jv @@ -0,0 +1,8 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +valuetype Percent oftype decimal { + constraints: [ZeroToHundredDecimal]; +} +constraint ZeroToHundredDecimal on decimal: value >= 0 and value <= 100; \ No newline at end of file diff --git a/libs/language-server/src/test/assets/builtin-block-type-definition/invalid-internal-block-type-duplicate-property.jv b/libs/language-server/src/test/assets/builtin-block-type-definition/invalid-internal-block-type-duplicate-property.jv new file mode 100644 index 00000000..b90f0708 --- /dev/null +++ b/libs/language-server/src/test/assets/builtin-block-type-definition/invalid-internal-block-type-duplicate-property.jv @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +builtin blocktype TestBlock { + input default oftype integer; + output default oftype integer; + + property testProp oftype text; + property testProp oftype text; +} diff --git a/libs/language-server/src/test/assets/builtin-block-type-definition/invalid-internal-block-type-multiple-inputs.jv b/libs/language-server/src/test/assets/builtin-block-type-definition/invalid-internal-block-type-multiple-inputs.jv new file mode 100644 index 00000000..8130a107 --- /dev/null +++ b/libs/language-server/src/test/assets/builtin-block-type-definition/invalid-internal-block-type-multiple-inputs.jv @@ -0,0 +1,10 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +builtin blocktype TestBlock { + input in1 oftype text; + input in2 oftype text; + + output out1 oftype text; +} diff --git a/libs/language-server/src/test/assets/builtin-block-type-definition/invalid-internal-block-type-multiple-outputs.jv b/libs/language-server/src/test/assets/builtin-block-type-definition/invalid-internal-block-type-multiple-outputs.jv new file mode 100644 index 00000000..ccf16d3d --- /dev/null +++ b/libs/language-server/src/test/assets/builtin-block-type-definition/invalid-internal-block-type-multiple-outputs.jv @@ -0,0 +1,10 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +builtin blocktype TestBlock { + output in1 oftype text; + output in2 oftype text; + + input in1 oftype text; +} diff --git a/libs/language-server/src/test/assets/builtin-block-type-definition/invalid-internal-block-type-no-input.jv b/libs/language-server/src/test/assets/builtin-block-type-definition/invalid-internal-block-type-no-input.jv new file mode 100644 index 00000000..df2379b6 --- /dev/null +++ b/libs/language-server/src/test/assets/builtin-block-type-definition/invalid-internal-block-type-no-input.jv @@ -0,0 +1,7 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +builtin blocktype TestBlock { + output default oftype Sheet; +} diff --git a/libs/language-server/src/test/assets/builtin-block-type-definition/invalid-internal-block-type-no-output.jv b/libs/language-server/src/test/assets/builtin-block-type-definition/invalid-internal-block-type-no-output.jv new file mode 100644 index 00000000..27c3932c --- /dev/null +++ b/libs/language-server/src/test/assets/builtin-block-type-definition/invalid-internal-block-type-no-output.jv @@ -0,0 +1,7 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +builtin blocktype TestBlock { + input default oftype Sheet; +} diff --git a/libs/language-server/src/test/assets/builtin-block-type-definition/invalid-internal-block-type-wrong-property-default-value.jv b/libs/language-server/src/test/assets/builtin-block-type-definition/invalid-internal-block-type-wrong-property-default-value.jv new file mode 100644 index 00000000..1d03572c --- /dev/null +++ b/libs/language-server/src/test/assets/builtin-block-type-definition/invalid-internal-block-type-wrong-property-default-value.jv @@ -0,0 +1,10 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +builtin blocktype TestBlock { + input default oftype integer; + output default oftype integer; + + property testProp oftype decimal: 'test'; +} diff --git a/libs/language-server/src/test/assets/builtin-block-type-definition/valid-internal-block-type-extractor.jv b/libs/language-server/src/test/assets/builtin-block-type-definition/valid-internal-block-type-extractor.jv new file mode 100644 index 00000000..cea96864 --- /dev/null +++ b/libs/language-server/src/test/assets/builtin-block-type-definition/valid-internal-block-type-extractor.jv @@ -0,0 +1,8 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +builtin blocktype TestExtractorBlock { + input default oftype none; + output default oftype integer; +} diff --git a/libs/language-server/src/test/assets/builtin-block-type-definition/valid-internal-block-type-loader.jv b/libs/language-server/src/test/assets/builtin-block-type-definition/valid-internal-block-type-loader.jv new file mode 100644 index 00000000..aed9c334 --- /dev/null +++ b/libs/language-server/src/test/assets/builtin-block-type-definition/valid-internal-block-type-loader.jv @@ -0,0 +1,8 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +builtin blocktype TestLoaderBlock { + input default oftype integer; + output default oftype none; +} diff --git a/libs/language-server/src/test/assets/builtin-block-type-definition/valid-internal-block-type-no-default-prop-values.jv b/libs/language-server/src/test/assets/builtin-block-type-definition/valid-internal-block-type-no-default-prop-values.jv new file mode 100644 index 00000000..e1cb6ad5 --- /dev/null +++ b/libs/language-server/src/test/assets/builtin-block-type-definition/valid-internal-block-type-no-default-prop-values.jv @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +builtin blocktype TestBlock { + input default oftype integer; + output default oftype integer; + + property testProp1 oftype text; + property testProp2 oftype text; +} diff --git a/libs/language-server/src/test/assets/builtin-block-type-definition/valid-internal-block-type-with-default-prop-values.jv b/libs/language-server/src/test/assets/builtin-block-type-definition/valid-internal-block-type-with-default-prop-values.jv new file mode 100644 index 00000000..e32f7aee --- /dev/null +++ b/libs/language-server/src/test/assets/builtin-block-type-definition/valid-internal-block-type-with-default-prop-values.jv @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +builtin blocktype TestBlock { + input default oftype integer; + output default oftype integer; + + property testProp1 oftype text: 'test1'; + property testProp2 oftype decimal: 3.14; +} diff --git a/libs/language-server/src/test/assets/column-id/invalid-column-id-lower-case.jv b/libs/language-server/src/test/assets/column-id/invalid-column-id-lower-case.jv new file mode 100644 index 00000000..74e3daf4 --- /dev/null +++ b/libs/language-server/src/test/assets/column-id/invalid-column-id-lower-case.jv @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype TestProperty { + textProperty: column test; + } +} diff --git a/libs/language-server/src/test/assets/column-id/valid-column-id-asterix.jv b/libs/language-server/src/test/assets/column-id/valid-column-id-asterix.jv new file mode 100644 index 00000000..32737714 --- /dev/null +++ b/libs/language-server/src/test/assets/column-id/valid-column-id-asterix.jv @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype TestProperty { + textProperty: column *; + } +} diff --git a/libs/language-server/src/test/assets/column-id/valid-column-id-capital-letters.jv b/libs/language-server/src/test/assets/column-id/valid-column-id-capital-letters.jv new file mode 100644 index 00000000..08b450dd --- /dev/null +++ b/libs/language-server/src/test/assets/column-id/valid-column-id-capital-letters.jv @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype TestProperty { + textProperty: column TEST; + } +} diff --git a/libs/language-server/src/test/assets/composite-block-type-definition/invalid-block-as-multiple-pipe-inputs.jv b/libs/language-server/src/test/assets/composite-block-type-definition/invalid-block-as-multiple-pipe-inputs.jv new file mode 100644 index 00000000..8d5a20f5 --- /dev/null +++ b/libs/language-server/src/test/assets/composite-block-type-definition/invalid-block-as-multiple-pipe-inputs.jv @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +composite blocktype TestBlockType { + input inPort oftype None; + output outPort oftype None; + + block BlockTo oftype TestTableLoader { } + + block BlockFrom1 oftype TestFileExtractor { } + + block BlockFrom2 oftype TestFileExtractor { } + + inPort -> BlockFrom1 -> BlockTo -> outPort; + inPort -> BlockFrom2 -> BlockTo -> outPort; +} + +builtin blocktype TestFileExtractor { + input inPort oftype None; + output outPort oftype Table; +} + +builtin blocktype TestTableLoader { + input inPort oftype Table; + output outPort oftype None; +} diff --git a/libs/language-server/src/test/assets/composite-block-type-definition/invalid-composite-block-type-multiple-pipelines.jv b/libs/language-server/src/test/assets/composite-block-type-definition/invalid-composite-block-type-multiple-pipelines.jv new file mode 100644 index 00000000..8ddd60f5 --- /dev/null +++ b/libs/language-server/src/test/assets/composite-block-type-definition/invalid-composite-block-type-multiple-pipelines.jv @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +composite blocktype TestBlock { + + input inputName oftype None; + output outputName oftype TextFile; + + block FileExtractor oftype HttpExtractor { url: 'url'; } + block FileTextInterpreter1 oftype TextFileInterpreter {} + block FileTextInterpreter2 oftype TextFileInterpreter {} + + inputName + ->FileExtractor + ->FileTextInterpreter1 + ->outputName; + + inputName + ->FileExtractor + ->FileTextInterpreter2 + ->outputName; +} \ No newline at end of file diff --git a/libs/language-server/src/test/assets/composite-block-type-definition/invalid-composite-block-type-no-pipeline.jv b/libs/language-server/src/test/assets/composite-block-type-definition/invalid-composite-block-type-no-pipeline.jv new file mode 100644 index 00000000..638e87ef --- /dev/null +++ b/libs/language-server/src/test/assets/composite-block-type-definition/invalid-composite-block-type-no-pipeline.jv @@ -0,0 +1,10 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +composite blocktype TestBlock { + property test oftype text; + + input inputName oftype None; + output outputName oftype Sheet; +} \ No newline at end of file diff --git a/libs/language-server/src/test/assets/composite-block-type-definition/invalid-unconnected-block.jv b/libs/language-server/src/test/assets/composite-block-type-definition/invalid-unconnected-block.jv new file mode 100644 index 00000000..252667e3 --- /dev/null +++ b/libs/language-server/src/test/assets/composite-block-type-definition/invalid-unconnected-block.jv @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +composite blocktype TestBlockType { + input inPort oftype Table; + output outPort oftype File; + + block TestExtractor1 oftype TestFileExtractor { } + block TestExtractor2 oftype TestFileExtractor { } + + inPort -> TestExtractor1 -> outPort; +} + +builtin blocktype TestFileExtractor { + input inPort oftype Table; + output outPort oftype File; +} diff --git a/libs/language-server/src/test/assets/composite-block-type-definition/valid-composite-block-type-extractor.jv b/libs/language-server/src/test/assets/composite-block-type-definition/valid-composite-block-type-extractor.jv new file mode 100644 index 00000000..01c3e975 --- /dev/null +++ b/libs/language-server/src/test/assets/composite-block-type-definition/valid-composite-block-type-extractor.jv @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +composite blocktype CSVExtractor { + property url oftype text; + property delimiter oftype text: ','; + property enclosing oftype text: ''; + property enclosingEscape oftype text: ''; + + input inputName oftype None; + output outputName oftype Sheet; + + block FileExtractor oftype HttpExtractor { url: url; } + block FileTextInterpreter oftype TextFileInterpreter {} + + block FileCSVInterpreter oftype CSVInterpreter { + delimiter: delimiter; + enclosing: enclosing; + enclosingEscape: enclosingEscape; + } + + inputName + ->FileExtractor + ->FileTextInterpreter + ->FileCSVInterpreter + ->outputName; +} \ No newline at end of file diff --git a/libs/language-server/src/test/assets/composite-block-type-definition/valid-composite-block-type-recursive.jv b/libs/language-server/src/test/assets/composite-block-type-definition/valid-composite-block-type-recursive.jv new file mode 100644 index 00000000..dfb2b5a0 --- /dev/null +++ b/libs/language-server/src/test/assets/composite-block-type-definition/valid-composite-block-type-recursive.jv @@ -0,0 +1,42 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +composite blocktype TextFileExtractor { + property url oftype text; + + input inputName oftype None; + output outputName oftype File; + + block FileExtractor oftype HttpExtractor { url: url; } + + block FileTextInterpreter oftype TextFileInterpreter {} + + inputName + ->FileExtractor + ->FileTextInterpreter + ->outputName; +} + +composite blocktype CSVExtractor { + property url oftype text; + property delimiter oftype text: ','; + property enclosing oftype text: ''; + property enclosingEscape oftype text: ''; + + input inputName oftype None; + output outputName oftype Sheet; + + block TextFileExtractor oftype TextFileExtractor { url: url; } + + block FileCSVInterpreter oftype CSVInterpreter { + delimiter: delimiter; + enclosing: enclosing; + enclosingEscape: enclosingEscape; + } + + inputName + ->TextFileExtractor + ->FileCSVInterpreter + ->outputName; +} \ No newline at end of file diff --git a/libs/language-server/src/test/assets/expression-constraint-definition/invalid-incompatible-type.jv b/libs/language-server/src/test/assets/expression-constraint-definition/invalid-incompatible-type.jv new file mode 100644 index 00000000..e4421533 --- /dev/null +++ b/libs/language-server/src/test/assets/expression-constraint-definition/invalid-incompatible-type.jv @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +constraint Constraint on text: + value.length + 10; diff --git a/libs/language-server/src/test/assets/expression-constraint-definition/valid-simplify-info.jv b/libs/language-server/src/test/assets/expression-constraint-definition/valid-simplify-info.jv new file mode 100644 index 00000000..70505e13 --- /dev/null +++ b/libs/language-server/src/test/assets/expression-constraint-definition/valid-simplify-info.jv @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +constraint Constraint on text: + value.length == (10 + -2); diff --git a/libs/language-server/src/test/assets/expression-constraint-definition/valid-text-constraint.jv b/libs/language-server/src/test/assets/expression-constraint-definition/valid-text-constraint.jv new file mode 100644 index 00000000..3562cf13 --- /dev/null +++ b/libs/language-server/src/test/assets/expression-constraint-definition/valid-text-constraint.jv @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +constraint Constraint on text: + value.length == 10; diff --git a/libs/language-server/src/test/assets/jayvee-model/invalid-duplicate-name-with-builtin-value-type.jv b/libs/language-server/src/test/assets/jayvee-model/invalid-duplicate-name-with-builtin-value-type.jv new file mode 100644 index 00000000..f062b6b9 --- /dev/null +++ b/libs/language-server/src/test/assets/jayvee-model/invalid-duplicate-name-with-builtin-value-type.jv @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +valuetype DuplicateValuetype oftype text { + constraints: [ + Constraint, + ]; +} + +builtin valuetype DuplicateValuetype; diff --git a/libs/language-server/src/test/assets/jayvee-model/invalid-non-unique-block-types.jv b/libs/language-server/src/test/assets/jayvee-model/invalid-non-unique-block-types.jv new file mode 100644 index 00000000..cb472481 --- /dev/null +++ b/libs/language-server/src/test/assets/jayvee-model/invalid-non-unique-block-types.jv @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +builtin blocktype TestBlock { + input default oftype text; +} + +builtin blocktype TestBlock { + input default oftype text; +} diff --git a/libs/language-server/src/test/assets/jayvee-model/invalid-non-unique-constraints.jv b/libs/language-server/src/test/assets/jayvee-model/invalid-non-unique-constraints.jv new file mode 100644 index 00000000..4f7ed80d --- /dev/null +++ b/libs/language-server/src/test/assets/jayvee-model/invalid-non-unique-constraints.jv @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +constraint Constraint on text: + value.length == 10; + +constraint Constraint on text: + value.length == 10; diff --git a/libs/language-server/src/test/assets/jayvee-model/invalid-non-unique-pipelines.jv b/libs/language-server/src/test/assets/jayvee-model/invalid-non-unique-pipelines.jv new file mode 100644 index 00000000..1f7c3598 --- /dev/null +++ b/libs/language-server/src/test/assets/jayvee-model/invalid-non-unique-pipelines.jv @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block TestExtractor oftype TestFileExtractor { + } +} + +pipeline Pipeline { + block TestExtractor oftype TestFileExtractor { + } +} diff --git a/libs/language-server/src/test/assets/jayvee-model/invalid-non-unique-transforms.jv b/libs/language-server/src/test/assets/jayvee-model/invalid-non-unique-transforms.jv new file mode 100644 index 00000000..8b8b0b38 --- /dev/null +++ b/libs/language-server/src/test/assets/jayvee-model/invalid-non-unique-transforms.jv @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +transform Transform { + from inputParam oftype decimal; + to result oftype integer; + + result: floor(inputParam); +} + +transform Transform { + from inputParam oftype decimal; + to inputParam oftype integer; + + result: ceil(inputParam); +} diff --git a/libs/language-server/src/test/assets/jayvee-model/invalid-non-unique-value-types.jv b/libs/language-server/src/test/assets/jayvee-model/invalid-non-unique-value-types.jv new file mode 100644 index 00000000..4218ad1a --- /dev/null +++ b/libs/language-server/src/test/assets/jayvee-model/invalid-non-unique-value-types.jv @@ -0,0 +1,15 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +valuetype ValueType oftype text { + constraints: [ + Constraint, + ]; +} + +valuetype ValueType oftype text { + constraints: [ + Constraint, + ]; +} diff --git a/libs/language-server/src/test/assets/pipe-definition/invalid-output-block-as-input.jv b/libs/language-server/src/test/assets/pipe-definition/invalid-output-block-as-input.jv new file mode 100644 index 00000000..1bec9407 --- /dev/null +++ b/libs/language-server/src/test/assets/pipe-definition/invalid-output-block-as-input.jv @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block BlockTo oftype TestFileLoader { + } + + block BlockFrom oftype TestFileExtractor { + } + + BlockTo -> BlockFrom; +} + +builtin blocktype TestFileExtractor { + input inPort oftype None; + output outPort oftype File; +} + +builtin blocktype TestFileLoader { + input inPort oftype File; + output outPort oftype None; +} diff --git a/libs/language-server/src/test/assets/pipe-definition/invalid-pipe-between-block-types.jv b/libs/language-server/src/test/assets/pipe-definition/invalid-pipe-between-block-types.jv new file mode 100644 index 00000000..65cc3933 --- /dev/null +++ b/libs/language-server/src/test/assets/pipe-definition/invalid-pipe-between-block-types.jv @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block TestExtractor oftype TestFileExtractor { + } + + block TestLoader oftype TestTableLoader { + } + + TestExtractor -> TestLoader; +} + +builtin blocktype TestFileExtractor { + input inPort oftype None; + output outPort oftype File; +} + +builtin blocktype TestTableLoader { + input inPort oftype Table; + output outPort oftype None; +} \ No newline at end of file diff --git a/libs/language-server/src/test/assets/pipe-definition/valid-undefined-block.jv b/libs/language-server/src/test/assets/pipe-definition/valid-undefined-block.jv new file mode 100644 index 00000000..a6ba9411 --- /dev/null +++ b/libs/language-server/src/test/assets/pipe-definition/valid-undefined-block.jv @@ -0,0 +1,7 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + TestExtractor -> TestLoader; +} diff --git a/libs/language-server/src/test/assets/pipe-definition/valid-unknown-block-type.jv b/libs/language-server/src/test/assets/pipe-definition/valid-unknown-block-type.jv new file mode 100644 index 00000000..513992e0 --- /dev/null +++ b/libs/language-server/src/test/assets/pipe-definition/valid-unknown-block-type.jv @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block UnknownOutput oftype UnknownOutputType { + } + + block UnknownInput oftype UnknownInputType { + } + + UnknownOutput -> UnknownInput; +} diff --git a/libs/language-server/src/test/assets/pipeline-definition/invalid-block-as-multiple-pipe-inputs.jv b/libs/language-server/src/test/assets/pipeline-definition/invalid-block-as-multiple-pipe-inputs.jv new file mode 100644 index 00000000..5347e081 --- /dev/null +++ b/libs/language-server/src/test/assets/pipeline-definition/invalid-block-as-multiple-pipe-inputs.jv @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block BlockTo oftype TestTableLoader { } + + block BlockFrom1 oftype TestFileExtractor { } + + block BlockFrom2 oftype TestFileExtractor { } + + BlockFrom1 -> BlockTo; + + BlockFrom2 -> BlockTo; +} + +builtin blocktype TestFileExtractor { + input inPort oftype None; + output outPort oftype Table; +} + +builtin blocktype TestTableLoader { + input inPort oftype Table; + output outPort oftype None; +} diff --git a/libs/language-server/src/test/assets/pipeline-definition/invalid-empty-pipeline.jv b/libs/language-server/src/test/assets/pipeline-definition/invalid-empty-pipeline.jv new file mode 100644 index 00000000..95f537fd --- /dev/null +++ b/libs/language-server/src/test/assets/pipeline-definition/invalid-empty-pipeline.jv @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { +} diff --git a/libs/language-server/src/test/assets/pipeline-definition/invalid-missing-pipe.jv b/libs/language-server/src/test/assets/pipeline-definition/invalid-missing-pipe.jv new file mode 100644 index 00000000..26af22d9 --- /dev/null +++ b/libs/language-server/src/test/assets/pipeline-definition/invalid-missing-pipe.jv @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block TestExtractor oftype TestFileExtractor { + } +} + +builtin blocktype TestFileExtractor { + input inPort oftype None; + output outPort oftype File; +} \ No newline at end of file diff --git a/libs/language-server/src/test/assets/pipeline-definition/invalid-pipeline-only-blocks.jv b/libs/language-server/src/test/assets/pipeline-definition/invalid-pipeline-only-blocks.jv new file mode 100644 index 00000000..3c6115bf --- /dev/null +++ b/libs/language-server/src/test/assets/pipeline-definition/invalid-pipeline-only-blocks.jv @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block TestExtractor oftype TestFileExtractor {} +} + +builtin blocktype TestFileExtractor { + input inPort oftype None; + output outPort oftype File; +} \ No newline at end of file diff --git a/libs/language-server/src/test/assets/pipeline-definition/valid-pipeline.jv b/libs/language-server/src/test/assets/pipeline-definition/valid-pipeline.jv new file mode 100644 index 00000000..c63c7e30 --- /dev/null +++ b/libs/language-server/src/test/assets/pipeline-definition/valid-pipeline.jv @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + TestExtractor -> TestLoader; + + block TestExtractor oftype TestFileExtractor {} + block TestLoader oftype TestFileLoader {} +} + +builtin blocktype TestFileExtractor { + input inPort oftype None; + output outPort oftype File; +} + +builtin blocktype TestFileLoader { + input inPort oftype File; + output outPort oftype None; +} \ No newline at end of file diff --git a/libs/language-server/src/test/assets/property-assignment/block-type-specific/archive-interpreter/invalid-invalid-archivetype-param.jv b/libs/language-server/src/test/assets/property-assignment/block-type-specific/archive-interpreter/invalid-invalid-archivetype-param.jv new file mode 100644 index 00000000..e0d6edae --- /dev/null +++ b/libs/language-server/src/test/assets/property-assignment/block-type-specific/archive-interpreter/invalid-invalid-archivetype-param.jv @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype ArchiveInterpreter { + archiveType: 'invalid'; + } + + block TestExtractor oftype TestFileExtractor { + } + + block TestLoader oftype TestSheetLoader { + } + + TestExtractor -> Test -> TestLoader; +} diff --git a/libs/language-server/src/test/assets/property-assignment/block-type-specific/archive-interpreter/valid-valid-archivetype-param.jv b/libs/language-server/src/test/assets/property-assignment/block-type-specific/archive-interpreter/valid-valid-archivetype-param.jv new file mode 100644 index 00000000..816879c0 --- /dev/null +++ b/libs/language-server/src/test/assets/property-assignment/block-type-specific/archive-interpreter/valid-valid-archivetype-param.jv @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype ArchiveInterpreter { + archiveType: 'zip'; + } + + block TestExtractor oftype TestFileExtractor { + } + + block TestLoader oftype TestSheetLoader { + } + + TestExtractor -> Test -> TestLoader; +} diff --git a/libs/language-server/src/test/assets/property-assignment/block-type-specific/cell-writer/invalid-wrong-at-dimension.jv b/libs/language-server/src/test/assets/property-assignment/block-type-specific/cell-writer/invalid-wrong-at-dimension.jv new file mode 100644 index 00000000..d8154bf7 --- /dev/null +++ b/libs/language-server/src/test/assets/property-assignment/block-type-specific/cell-writer/invalid-wrong-at-dimension.jv @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype CellWriter { + at: range A1:B2; + write: ['the', 'values', 'to', 'write']; + } + + block TestExtractor oftype TestSheetExtractor { + } + + block TestLoader oftype TestSheetLoader { + } + + TestExtractor -> Test -> TestLoader; +} diff --git a/libs/language-server/src/test/assets/property-assignment/block-type-specific/cell-writer/valid-one-dimensional-at-value.jv b/libs/language-server/src/test/assets/property-assignment/block-type-specific/cell-writer/valid-one-dimensional-at-value.jv new file mode 100644 index 00000000..f07269c4 --- /dev/null +++ b/libs/language-server/src/test/assets/property-assignment/block-type-specific/cell-writer/valid-one-dimensional-at-value.jv @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype CellWriter { + at: range A1:A2; + write: ['the', 'values']; + } + + block TestExtractor oftype TestSheetExtractor { + } + + block TestLoader oftype TestSheetLoader { + } + + TestExtractor -> Test -> TestLoader; +} diff --git a/libs/language-server/src/test/assets/property-assignment/block-type-specific/column-deleter/invalid-partial-column-delete.jv b/libs/language-server/src/test/assets/property-assignment/block-type-specific/column-deleter/invalid-partial-column-delete.jv new file mode 100644 index 00000000..981dab3a --- /dev/null +++ b/libs/language-server/src/test/assets/property-assignment/block-type-specific/column-deleter/invalid-partial-column-delete.jv @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype ColumnDeleter { + delete: [range A1:A3]; + } + + block TestExtractor oftype TestSheetExtractor { + } + + block TestLoader oftype TestSheetLoader { + } + + TestExtractor -> Test -> TestLoader; +} diff --git a/libs/language-server/src/test/assets/property-assignment/block-type-specific/column-deleter/valid-column-delete.jv b/libs/language-server/src/test/assets/property-assignment/block-type-specific/column-deleter/valid-column-delete.jv new file mode 100644 index 00000000..29da54be --- /dev/null +++ b/libs/language-server/src/test/assets/property-assignment/block-type-specific/column-deleter/valid-column-delete.jv @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype ColumnDeleter { + delete: [column A]; + } + + block TestExtractor oftype TestSheetExtractor { + } + + block TestLoader oftype TestSheetLoader { + } + + TestExtractor -> Test -> TestLoader; +} diff --git a/libs/language-server/src/test/assets/property-assignment/block-type-specific/gtfs-rt-interpreter/invalid-invalid-entity-param.jv b/libs/language-server/src/test/assets/property-assignment/block-type-specific/gtfs-rt-interpreter/invalid-invalid-entity-param.jv new file mode 100644 index 00000000..abcdd270 --- /dev/null +++ b/libs/language-server/src/test/assets/property-assignment/block-type-specific/gtfs-rt-interpreter/invalid-invalid-entity-param.jv @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype GtfsRTInterpreter { + entity: 'invalid'; + } + + block TestExtractor oftype TestFileExtractor { + } + + block TestLoader oftype TestSheetLoader { + } + + TestExtractor -> Test -> TestLoader; +} diff --git a/libs/language-server/src/test/assets/property-assignment/block-type-specific/gtfs-rt-interpreter/valid-valid-entity-param.jv b/libs/language-server/src/test/assets/property-assignment/block-type-specific/gtfs-rt-interpreter/valid-valid-entity-param.jv new file mode 100644 index 00000000..98acd3a9 --- /dev/null +++ b/libs/language-server/src/test/assets/property-assignment/block-type-specific/gtfs-rt-interpreter/valid-valid-entity-param.jv @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype GtfsRTInterpreter { + entity: 'alert'; + } + + block TestExtractor oftype TestFileExtractor { + } + + block TestLoader oftype TestSheetLoader { + } + + TestExtractor -> Test -> TestLoader; +} diff --git a/libs/language-server/src/test/assets/property-assignment/block-type-specific/http-extractor/invalid-invalid-retries-param.jv b/libs/language-server/src/test/assets/property-assignment/block-type-specific/http-extractor/invalid-invalid-retries-param.jv new file mode 100644 index 00000000..50ab17c8 --- /dev/null +++ b/libs/language-server/src/test/assets/property-assignment/block-type-specific/http-extractor/invalid-invalid-retries-param.jv @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype HttpExtractor { + retries: -1; + url: 'http://some-url.de'; + } + block TestLoader oftype TestLoader { + } + + Test -> TestLoader; +} diff --git a/libs/language-server/src/test/assets/property-assignment/block-type-specific/http-extractor/invalid-invalid-retryBackoffMilliseconds-param.jv b/libs/language-server/src/test/assets/property-assignment/block-type-specific/http-extractor/invalid-invalid-retryBackoffMilliseconds-param.jv new file mode 100644 index 00000000..96445ed4 --- /dev/null +++ b/libs/language-server/src/test/assets/property-assignment/block-type-specific/http-extractor/invalid-invalid-retryBackoffMilliseconds-param.jv @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype HttpExtractor { + retryBackoffMilliseconds: 999; + url: 'http://some-url.de'; + } + block TestLoader oftype TestLoader { + } + + Test -> TestLoader; +} diff --git a/libs/language-server/src/test/assets/property-assignment/block-type-specific/http-extractor/invalid-invalid-retryBackoffStrategy-param.jv b/libs/language-server/src/test/assets/property-assignment/block-type-specific/http-extractor/invalid-invalid-retryBackoffStrategy-param.jv new file mode 100644 index 00000000..072ea9a0 --- /dev/null +++ b/libs/language-server/src/test/assets/property-assignment/block-type-specific/http-extractor/invalid-invalid-retryBackoffStrategy-param.jv @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype HttpExtractor { + retryBackoffStrategy: 'invalid'; + url: 'http://some-url.de'; + } + block TestLoader oftype TestLoader { + } + + Test -> TestLoader; +} diff --git a/libs/language-server/src/test/assets/property-assignment/block-type-specific/http-extractor/valid-valid-retries-param.jv b/libs/language-server/src/test/assets/property-assignment/block-type-specific/http-extractor/valid-valid-retries-param.jv new file mode 100644 index 00000000..b9f25153 --- /dev/null +++ b/libs/language-server/src/test/assets/property-assignment/block-type-specific/http-extractor/valid-valid-retries-param.jv @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype HttpExtractor { + retries: 3; + url: 'http://some-url.de'; + } + block TestLoader oftype TestLoader { + } + + Test -> TestLoader; +} diff --git a/libs/language-server/src/test/assets/property-assignment/block-type-specific/http-extractor/valid-valid-retryBackoffMilliseconds-param.jv b/libs/language-server/src/test/assets/property-assignment/block-type-specific/http-extractor/valid-valid-retryBackoffMilliseconds-param.jv new file mode 100644 index 00000000..7fec1dec --- /dev/null +++ b/libs/language-server/src/test/assets/property-assignment/block-type-specific/http-extractor/valid-valid-retryBackoffMilliseconds-param.jv @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype HttpExtractor { + retryBackoffMilliseconds: 5000; + url: 'http://some-url.de'; + } + block TestLoader oftype TestLoader { + } + + Test -> TestLoader; +} diff --git a/libs/language-server/src/test/assets/property-assignment/block-type-specific/http-extractor/valid-valid-retryBackoffStrategy-param.jv b/libs/language-server/src/test/assets/property-assignment/block-type-specific/http-extractor/valid-valid-retryBackoffStrategy-param.jv new file mode 100644 index 00000000..33b56c9c --- /dev/null +++ b/libs/language-server/src/test/assets/property-assignment/block-type-specific/http-extractor/valid-valid-retryBackoffStrategy-param.jv @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype HttpExtractor { + retryBackoffStrategy: 'linear'; + url: 'http://some-url.de'; + } + block TestLoader oftype TestLoader { + } + + Test -> TestLoader; +} diff --git a/libs/language-server/src/test/assets/property-assignment/block-type-specific/local-file-extractor/invalid-invalid-filepath-param.jv b/libs/language-server/src/test/assets/property-assignment/block-type-specific/local-file-extractor/invalid-invalid-filepath-param.jv new file mode 100644 index 00000000..0d7cd614 --- /dev/null +++ b/libs/language-server/src/test/assets/property-assignment/block-type-specific/local-file-extractor/invalid-invalid-filepath-param.jv @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype LocalFileExtractor { + filePath: './../invalid-path-traversal.csv'; + } + + block TestExtractor oftype TestFileExtractor { + } + + block TestLoader oftype TestSheetLoader { + } + + TestExtractor -> Test -> TestLoader; +} diff --git a/libs/language-server/src/test/assets/property-assignment/block-type-specific/local-file-extractor/valid-valid-filepath-param.jv b/libs/language-server/src/test/assets/property-assignment/block-type-specific/local-file-extractor/valid-valid-filepath-param.jv new file mode 100644 index 00000000..affdbc18 --- /dev/null +++ b/libs/language-server/src/test/assets/property-assignment/block-type-specific/local-file-extractor/valid-valid-filepath-param.jv @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype LocalFileExtractor { + filePath: './valid-path-traversal.csv'; + } + + block TestExtractor oftype TestFileExtractor { + } + + block TestLoader oftype TestSheetLoader { + } + + TestExtractor -> Test -> TestLoader; +} diff --git a/libs/language-server/src/test/assets/property-assignment/block-type-specific/row-deleter/invalid-partial-row-delete.jv b/libs/language-server/src/test/assets/property-assignment/block-type-specific/row-deleter/invalid-partial-row-delete.jv new file mode 100644 index 00000000..80c2e8e3 --- /dev/null +++ b/libs/language-server/src/test/assets/property-assignment/block-type-specific/row-deleter/invalid-partial-row-delete.jv @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype RowDeleter { + delete: [range A1:C1]; + } + + block TestExtractor oftype TestSheetExtractor { + } + + block TestLoader oftype TestSheetLoader { + } + + TestExtractor -> Test -> TestLoader; +} diff --git a/libs/language-server/src/test/assets/property-assignment/block-type-specific/row-deleter/valid-row-delete.jv b/libs/language-server/src/test/assets/property-assignment/block-type-specific/row-deleter/valid-row-delete.jv new file mode 100644 index 00000000..673fe5f6 --- /dev/null +++ b/libs/language-server/src/test/assets/property-assignment/block-type-specific/row-deleter/valid-row-delete.jv @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype RowDeleter { + delete: [row 1]; + } + + block TestExtractor oftype TestSheetExtractor { + } + + block TestLoader oftype TestSheetLoader { + } + + TestExtractor -> Test -> TestLoader; +} diff --git a/libs/language-server/src/test/assets/property-assignment/block-type-specific/table-interpreter/invalid-non-unique-column-names.jv b/libs/language-server/src/test/assets/property-assignment/block-type-specific/table-interpreter/invalid-non-unique-column-names.jv new file mode 100644 index 00000000..6cea9044 --- /dev/null +++ b/libs/language-server/src/test/assets/property-assignment/block-type-specific/table-interpreter/invalid-non-unique-column-names.jv @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype TableInterpreter { + columns: [ + "name" oftype text, + "name" oftype integer, + ]; + } + + block TestExtractor oftype TestSheetExtractor { + } + + block TestLoader oftype TestTableLoader { + } + + TestExtractor -> Test -> TestLoader; +} diff --git a/libs/language-server/src/test/assets/property-assignment/block-type-specific/table-interpreter/valid-correct-table.jv b/libs/language-server/src/test/assets/property-assignment/block-type-specific/table-interpreter/valid-correct-table.jv new file mode 100644 index 00000000..06cc9f9d --- /dev/null +++ b/libs/language-server/src/test/assets/property-assignment/block-type-specific/table-interpreter/valid-correct-table.jv @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype TableInterpreter { + columns: [ + "name" oftype text, + "version" oftype integer, + ]; + } + + block TestExtractor oftype TestSheetExtractor { + } + + block TestLoader oftype TestTableLoader { + } + + TestExtractor -> Test -> TestLoader; +} diff --git a/libs/language-server/src/test/assets/property-assignment/block-type-specific/text-file-interpreter/invalid-invalid-encoding-param.jv b/libs/language-server/src/test/assets/property-assignment/block-type-specific/text-file-interpreter/invalid-invalid-encoding-param.jv new file mode 100644 index 00000000..4a9dbe87 --- /dev/null +++ b/libs/language-server/src/test/assets/property-assignment/block-type-specific/text-file-interpreter/invalid-invalid-encoding-param.jv @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype TextFileInterpreter { + encoding: 'invalid'; + } + + block TestExtractor oftype TestFileExtractor { + } + + block TestLoader oftype TestTextFileLoader { + } + + TestExtractor -> Test -> TestLoader; +} diff --git a/libs/language-server/src/test/assets/property-assignment/block-type-specific/text-file-interpreter/valid-utf8-encoding-param.jv b/libs/language-server/src/test/assets/property-assignment/block-type-specific/text-file-interpreter/valid-utf8-encoding-param.jv new file mode 100644 index 00000000..7537150f --- /dev/null +++ b/libs/language-server/src/test/assets/property-assignment/block-type-specific/text-file-interpreter/valid-utf8-encoding-param.jv @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype TextFileInterpreter { + encoding: 'utf8'; + } + + block TestExtractor oftype TestFileExtractor { + } + + block TestLoader oftype TestTextFileLoader { + } + + TestExtractor -> Test -> TestLoader; +} diff --git a/libs/language-server/src/test/assets/property-assignment/block-type-specific/text-line-deleter/invalid-line-less-or-equal-zero.jv b/libs/language-server/src/test/assets/property-assignment/block-type-specific/text-line-deleter/invalid-line-less-or-equal-zero.jv new file mode 100644 index 00000000..68f2df26 --- /dev/null +++ b/libs/language-server/src/test/assets/property-assignment/block-type-specific/text-line-deleter/invalid-line-less-or-equal-zero.jv @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype TextLineDeleter { + lines: [2,3,0,-1,-20]; + } + + block TestExtractor oftype TestTextFileExtractor { + } + + block TestLoader oftype TestTextFileLoader { + } + + TestExtractor -> Test -> TestLoader; +} diff --git a/libs/language-server/src/test/assets/property-assignment/block-type-specific/text-line-deleter/valid-postive-line-number.jv b/libs/language-server/src/test/assets/property-assignment/block-type-specific/text-line-deleter/valid-postive-line-number.jv new file mode 100644 index 00000000..fc770941 --- /dev/null +++ b/libs/language-server/src/test/assets/property-assignment/block-type-specific/text-line-deleter/valid-postive-line-number.jv @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype TextLineDeleter { + lines: [2,3]; + } + + block TestExtractor oftype TestTextFileExtractor { + } + + block TestLoader oftype TestTextFileLoader { + } + + TestExtractor -> Test -> TestLoader; +} diff --git a/libs/language-server/src/test/assets/property-assignment/block-type-specific/text-range-selector/invalid-lineFrom-less-or-equal-zero.jv b/libs/language-server/src/test/assets/property-assignment/block-type-specific/text-range-selector/invalid-lineFrom-less-or-equal-zero.jv new file mode 100644 index 00000000..6bce3111 --- /dev/null +++ b/libs/language-server/src/test/assets/property-assignment/block-type-specific/text-range-selector/invalid-lineFrom-less-or-equal-zero.jv @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype TextRangeSelector { + lineFrom: -1; + } + + block TestExtractor oftype TestTextFileExtractor { + } + + block TestLoader oftype TestTextFileLoader { + } + + TestExtractor -> Test -> TestLoader; +} diff --git a/libs/language-server/src/test/assets/property-assignment/block-type-specific/text-range-selector/invalid-lineTo-less-or-equal-zero.jv b/libs/language-server/src/test/assets/property-assignment/block-type-specific/text-range-selector/invalid-lineTo-less-or-equal-zero.jv new file mode 100644 index 00000000..8800e2c8 --- /dev/null +++ b/libs/language-server/src/test/assets/property-assignment/block-type-specific/text-range-selector/invalid-lineTo-less-or-equal-zero.jv @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype TextRangeSelector { + lineTo: -1; + } + + block TestExtractor oftype TestTextFileExtractor { + } + + block TestLoader oftype TestTextFileLoader { + } + + TestExtractor -> Test -> TestLoader; +} diff --git a/libs/language-server/src/test/assets/property-assignment/block-type-specific/text-range-selector/valid-postive-lineFrom-number.jv b/libs/language-server/src/test/assets/property-assignment/block-type-specific/text-range-selector/valid-postive-lineFrom-number.jv new file mode 100644 index 00000000..593c9316 --- /dev/null +++ b/libs/language-server/src/test/assets/property-assignment/block-type-specific/text-range-selector/valid-postive-lineFrom-number.jv @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype TextRangeSelector { + lineFrom: 2; + } + + block TestExtractor oftype TestTextFileExtractor { + } + + block TestLoader oftype TestTextFileLoader { + } + + TestExtractor -> Test -> TestLoader; +} diff --git a/libs/language-server/src/test/assets/property-assignment/block-type-specific/text-range-selector/valid-postive-lineTo-number.jv b/libs/language-server/src/test/assets/property-assignment/block-type-specific/text-range-selector/valid-postive-lineTo-number.jv new file mode 100644 index 00000000..b84fac00 --- /dev/null +++ b/libs/language-server/src/test/assets/property-assignment/block-type-specific/text-range-selector/valid-postive-lineTo-number.jv @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype TextRangeSelector { + lineTo: 2; + } + + block TestExtractor oftype TestTextFileExtractor { + } + + block TestLoader oftype TestTextFileLoader { + } + + TestExtractor -> Test -> TestLoader; +} diff --git a/libs/language-server/src/test/assets/property-assignment/constrainttype-specific/length-constraint/invalid-max-negative.jv b/libs/language-server/src/test/assets/property-assignment/constrainttype-specific/length-constraint/invalid-max-negative.jv new file mode 100644 index 00000000..52240e08 --- /dev/null +++ b/libs/language-server/src/test/assets/property-assignment/constrainttype-specific/length-constraint/invalid-max-negative.jv @@ -0,0 +1,8 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +constraint Test oftype LengthConstraint { + minLength: -3; + maxLength: -2; +} diff --git a/libs/language-server/src/test/assets/property-assignment/constrainttype-specific/length-constraint/invalid-min-negative.jv b/libs/language-server/src/test/assets/property-assignment/constrainttype-specific/length-constraint/invalid-min-negative.jv new file mode 100644 index 00000000..4f1b5356 --- /dev/null +++ b/libs/language-server/src/test/assets/property-assignment/constrainttype-specific/length-constraint/invalid-min-negative.jv @@ -0,0 +1,8 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +constraint Test oftype LengthConstraint { + minLength: -1; + maxLength: 4; +} diff --git a/libs/language-server/src/test/assets/property-assignment/invalid-property-type.jv b/libs/language-server/src/test/assets/property-assignment/invalid-property-type.jv new file mode 100644 index 00000000..f02bb85f --- /dev/null +++ b/libs/language-server/src/test/assets/property-assignment/invalid-property-type.jv @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype TestProperty { + textProperty: 2; + } +} + +builtin blocktype TestProperty { + input inPort oftype File; + output outPort oftype Table; + + property integerProperty oftype integer: 0; + property decimalProperty oftype integer: 0.0; + property textProperty oftype text; + property booleanProperty oftype boolean: false; + property regexProperty oftype Regex: /\r?\n/; + property textCollectionProperty oftype Collection<text>: []; + property valuetypeAssignmentProperty oftype ValuetypeAssignment: "test" oftype text; +} \ No newline at end of file diff --git a/libs/language-server/src/test/assets/property-assignment/invalid-runtime-property.jv b/libs/language-server/src/test/assets/property-assignment/invalid-runtime-property.jv new file mode 100644 index 00000000..29a0736b --- /dev/null +++ b/libs/language-server/src/test/assets/property-assignment/invalid-runtime-property.jv @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype TestProperty { + regexProperty: requires REGEX_ENV; + } +} + +builtin blocktype TestProperty { + input inPort oftype File; + output outPort oftype Table; + + property integerProperty oftype integer: 0; + property decimalProperty oftype integer: 0.0; + property textProperty oftype text; + property booleanProperty oftype boolean: false; + property regexProperty oftype Regex: /\r?\n/; + property textCollectionProperty oftype Collection<text>: []; + property valuetypeAssignmentProperty oftype ValuetypeAssignment: "test" oftype text; +} \ No newline at end of file diff --git a/libs/language-server/src/test/assets/property-assignment/invalid-unknown-property.jv b/libs/language-server/src/test/assets/property-assignment/invalid-unknown-property.jv new file mode 100644 index 00000000..cf0a8e94 --- /dev/null +++ b/libs/language-server/src/test/assets/property-assignment/invalid-unknown-property.jv @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype TestProperty { + unknownProperty: ''; + } +} + +builtin blocktype TestProperty { + input inPort oftype File; + output outPort oftype Table; + + property integerProperty oftype integer: 0; + property decimalProperty oftype integer: 0.0; + property textProperty oftype text; + property booleanProperty oftype boolean: false; + property regexProperty oftype Regex: /\r?\n/; + property textCollectionProperty oftype Collection<text>: []; + property valuetypeAssignmentProperty oftype ValuetypeAssignment: "test" oftype text; +} \ No newline at end of file diff --git a/libs/language-server/src/test/assets/property-assignment/valid-runtime-property.jv b/libs/language-server/src/test/assets/property-assignment/valid-runtime-property.jv new file mode 100644 index 00000000..6fddbd70 --- /dev/null +++ b/libs/language-server/src/test/assets/property-assignment/valid-runtime-property.jv @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype TestProperty { + textProperty: requires TEXT_ENV; + } +} + +builtin blocktype TestProperty { + input inPort oftype File; + output outPort oftype Table; + + property integerProperty oftype integer: 0; + property decimalProperty oftype integer: 0.0; + property textProperty oftype text; + property booleanProperty oftype boolean: false; + property regexProperty oftype Regex: /\r?\n/; + property textCollectionProperty oftype Collection<text>: []; + property valuetypeAssignmentProperty oftype ValuetypeAssignment: "test" oftype text; +} \ No newline at end of file diff --git a/libs/language-server/src/test/assets/property-assignment/valid-simplify-info-sub-expression.jv b/libs/language-server/src/test/assets/property-assignment/valid-simplify-info-sub-expression.jv new file mode 100644 index 00000000..78d58798 --- /dev/null +++ b/libs/language-server/src/test/assets/property-assignment/valid-simplify-info-sub-expression.jv @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype TestProperty { + integerProperty: 10 + (10 + 10); + } +} + +builtin blocktype TestProperty { + input inPort oftype File; + output outPort oftype Table; + + property integerProperty oftype integer: 0; + property decimalProperty oftype integer: 0.0; + property textProperty oftype text; + property booleanProperty oftype boolean: false; + property regexProperty oftype Regex: /\r?\n/; + property textCollectionProperty oftype Collection<text>: []; + property valuetypeAssignmentProperty oftype ValuetypeAssignment: "test" oftype text; +} \ No newline at end of file diff --git a/libs/language-server/src/test/assets/property-assignment/valid-simplify-info.jv b/libs/language-server/src/test/assets/property-assignment/valid-simplify-info.jv new file mode 100644 index 00000000..4a5e0756 --- /dev/null +++ b/libs/language-server/src/test/assets/property-assignment/valid-simplify-info.jv @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype TestProperty { + integerProperty: 345 + 674; + } +} + +builtin blocktype TestProperty { + input inPort oftype File; + output outPort oftype Table; + + property integerProperty oftype integer: 0; + property decimalProperty oftype integer: 0.0; + property textProperty oftype text; + property booleanProperty oftype boolean: false; + property regexProperty oftype Regex: /\r?\n/; + property textCollectionProperty oftype Collection<text>: []; + property valuetypeAssignmentProperty oftype ValuetypeAssignment: "test" oftype text; +} \ No newline at end of file diff --git a/libs/language-server/src/test/assets/property-assignment/valid-uneccessarysimplify-info.jv b/libs/language-server/src/test/assets/property-assignment/valid-uneccessarysimplify-info.jv new file mode 100644 index 00000000..a30fccf5 --- /dev/null +++ b/libs/language-server/src/test/assets/property-assignment/valid-uneccessarysimplify-info.jv @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype TestProperty { + integerProperty: -674; + } +} + +builtin blocktype TestProperty { + input inPort oftype File; + output outPort oftype Table; + + property integerProperty oftype integer: 0; + property decimalProperty oftype integer: 0.0; + property textProperty oftype text; + property booleanProperty oftype boolean: false; + property regexProperty oftype Regex: /\r?\n/; + property textCollectionProperty oftype Collection<text>: []; + property valuetypeAssignmentProperty oftype ValuetypeAssignment: "test" oftype text; +} \ No newline at end of file diff --git a/libs/language-server/src/test/assets/property-body/block-type-specific/cell-writer/invalid-write-length-does-not-match-cell-range.jv b/libs/language-server/src/test/assets/property-body/block-type-specific/cell-writer/invalid-write-length-does-not-match-cell-range.jv new file mode 100644 index 00000000..9384c99a --- /dev/null +++ b/libs/language-server/src/test/assets/property-body/block-type-specific/cell-writer/invalid-write-length-does-not-match-cell-range.jv @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype CellWriter { + at: range A1:A4; + write: ['values', 'to', 'write']; + } + + block TestExtractor oftype TestSheetExtractor { + } + + block TestLoader oftype TestSheetLoader { + } + + TestExtractor -> Test -> TestLoader; +} diff --git a/libs/language-server/src/test/assets/property-body/block-type-specific/cell-writer/valid-range-matches-array-length.jv b/libs/language-server/src/test/assets/property-body/block-type-specific/cell-writer/valid-range-matches-array-length.jv new file mode 100644 index 00000000..2d9a51d5 --- /dev/null +++ b/libs/language-server/src/test/assets/property-body/block-type-specific/cell-writer/valid-range-matches-array-length.jv @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype CellWriter { + at: range A1:A3; + write: ['values', 'to', 'write']; + } + + block TestExtractor oftype TestSheetExtractor { + } + + block TestLoader oftype TestSheetLoader { + } + + TestExtractor -> Test -> TestLoader; +} diff --git a/libs/language-server/src/test/assets/property-body/block-type-specific/table-transformer/invalid-input-columns-transform-port-missmatch.jv b/libs/language-server/src/test/assets/property-body/block-type-specific/table-transformer/invalid-input-columns-transform-port-missmatch.jv new file mode 100644 index 00000000..4179d0a0 --- /dev/null +++ b/libs/language-server/src/test/assets/property-body/block-type-specific/table-transformer/invalid-input-columns-transform-port-missmatch.jv @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + transform TestTransform { + from inputParam oftype decimal; + to result oftype integer; + + result: ceil(inputParam); + } + + block Test oftype TableTransformer { + inputColumns: ['input1', 'input2']; + outputColumn: 'output'; + use: TestTransform; + } + + block TestExtractor oftype TestTableExtractor { + } + + block TestLoader oftype TestTableLoader { + } + + TestExtractor -> Test -> TestLoader; +} diff --git a/libs/language-server/src/test/assets/property-body/block-type-specific/table-transformer/valid-correct-ports.jv b/libs/language-server/src/test/assets/property-body/block-type-specific/table-transformer/valid-correct-ports.jv new file mode 100644 index 00000000..c6da883d --- /dev/null +++ b/libs/language-server/src/test/assets/property-body/block-type-specific/table-transformer/valid-correct-ports.jv @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + transform TestTransform { + from inputParam oftype decimal; + to result oftype integer; + + result: ceil(inputParam); + } + + block Test oftype TableTransformer { + inputColumns: ['input1']; + outputColumn: 'output'; + use: TestTransform; + } + + block TestExtractor oftype TestTableExtractor { + } + + block TestLoader oftype TestTableLoader { + } + + TestExtractor -> Test -> TestLoader; +} diff --git a/libs/language-server/src/test/assets/property-body/block-type-specific/text-range-selector/invalid-lineFrom-greater-lineTo.jv b/libs/language-server/src/test/assets/property-body/block-type-specific/text-range-selector/invalid-lineFrom-greater-lineTo.jv new file mode 100644 index 00000000..cdd0a588 --- /dev/null +++ b/libs/language-server/src/test/assets/property-body/block-type-specific/text-range-selector/invalid-lineFrom-greater-lineTo.jv @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype TextRangeSelector { + lineFrom: 10; + lineTo: 1; + } + + block TestExtractor oftype TestTextFileExtractor { + } + + block TestLoader oftype TestTextFileLoader { + } + + TestExtractor -> Test -> TestLoader; +} diff --git a/libs/language-server/src/test/assets/property-body/block-type-specific/text-range-selector/valid-correct-range.jv b/libs/language-server/src/test/assets/property-body/block-type-specific/text-range-selector/valid-correct-range.jv new file mode 100644 index 00000000..3359596c --- /dev/null +++ b/libs/language-server/src/test/assets/property-body/block-type-specific/text-range-selector/valid-correct-range.jv @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype TextRangeSelector { + lineFrom: 1; + lineTo: 2; + } + + block TestExtractor oftype TestTextFileExtractor { + } + + block TestLoader oftype TestTextFileLoader { + } + + TestExtractor -> Test -> TestLoader; +} diff --git a/libs/language-server/src/test/assets/property-body/constrainttype-specific/length-constraint/invalid-min-greater-max.jv b/libs/language-server/src/test/assets/property-body/constrainttype-specific/length-constraint/invalid-min-greater-max.jv new file mode 100644 index 00000000..37d11fc4 --- /dev/null +++ b/libs/language-server/src/test/assets/property-body/constrainttype-specific/length-constraint/invalid-min-greater-max.jv @@ -0,0 +1,8 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +constraint Test oftype LengthConstraint { + minLength: 1; + maxLength: 0; +} diff --git a/libs/language-server/src/test/assets/property-body/constrainttype-specific/length-constraint/no-bound.jv b/libs/language-server/src/test/assets/property-body/constrainttype-specific/length-constraint/no-bound.jv new file mode 100644 index 00000000..e7302837 --- /dev/null +++ b/libs/language-server/src/test/assets/property-body/constrainttype-specific/length-constraint/no-bound.jv @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +constraint Test oftype LengthConstraint { +} diff --git a/libs/language-server/src/test/assets/property-body/constrainttype-specific/range-constraint/invalid-lower-above-upper.jv b/libs/language-server/src/test/assets/property-body/constrainttype-specific/range-constraint/invalid-lower-above-upper.jv new file mode 100644 index 00000000..4f927d98 --- /dev/null +++ b/libs/language-server/src/test/assets/property-body/constrainttype-specific/range-constraint/invalid-lower-above-upper.jv @@ -0,0 +1,8 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +constraint Test oftype RangeConstraint { + lowerBound: 101; + upperBound: 100; +} diff --git a/libs/language-server/src/test/assets/property-body/constrainttype-specific/range-constraint/invalid-missing-bound-inclusivity.jv b/libs/language-server/src/test/assets/property-body/constrainttype-specific/range-constraint/invalid-missing-bound-inclusivity.jv new file mode 100644 index 00000000..1d137ead --- /dev/null +++ b/libs/language-server/src/test/assets/property-body/constrainttype-specific/range-constraint/invalid-missing-bound-inclusivity.jv @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +constraint Test oftype RangeConstraint { + lowerBound: 100; + upperBound: 100; + lowerBoundInclusive: false; +} diff --git a/libs/language-server/src/test/assets/property-body/constrainttype-specific/range-constraint/no-bound.jv b/libs/language-server/src/test/assets/property-body/constrainttype-specific/range-constraint/no-bound.jv new file mode 100644 index 00000000..8247c354 --- /dev/null +++ b/libs/language-server/src/test/assets/property-body/constrainttype-specific/range-constraint/no-bound.jv @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +constraint Test oftype RangeConstraint { +} diff --git a/libs/language-server/src/test/assets/property-body/invalid-missing-property.jv b/libs/language-server/src/test/assets/property-body/invalid-missing-property.jv new file mode 100644 index 00000000..559cc719 --- /dev/null +++ b/libs/language-server/src/test/assets/property-body/invalid-missing-property.jv @@ -0,0 +1,15 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype TestProperty { + } +} + +builtin blocktype TestProperty { + input inPort oftype None; + output outPort oftype None; + + property textProperty oftype text; +} diff --git a/libs/language-server/src/test/assets/property-body/invalid-property-validation-failed.jv b/libs/language-server/src/test/assets/property-body/invalid-property-validation-failed.jv new file mode 100644 index 00000000..fc143ce1 --- /dev/null +++ b/libs/language-server/src/test/assets/property-body/invalid-property-validation-failed.jv @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype TestProperty { + textProperty: ''; + customValidationTextProperty: 'invalid'; + } +} + +builtin blocktype TestProperty { + input inPort oftype None; + output outPort oftype None; + + property textProperty oftype text; + property customValidationTextProperty oftype CustomValuetype; +} + +valuetype CustomValuetype oftype text { + constraints: [CustomConstraint]; +} + +constraint CustomConstraint on text: + value in ['valid']; diff --git a/libs/language-server/src/test/assets/property-body/valid-default-values.jv b/libs/language-server/src/test/assets/property-body/valid-default-values.jv new file mode 100644 index 00000000..3baabcc1 --- /dev/null +++ b/libs/language-server/src/test/assets/property-body/valid-default-values.jv @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype TestProperty { + textProperty: ''; + } +} + +builtin blocktype TestProperty { + input inPort oftype None; + output outPort oftype None; + + property textProperty oftype text; +} diff --git a/libs/language-server/src/test/assets/range-literal/invalid-range-literal-start-after-end.jv b/libs/language-server/src/test/assets/range-literal/invalid-range-literal-start-after-end.jv new file mode 100644 index 00000000..95439729 --- /dev/null +++ b/libs/language-server/src/test/assets/range-literal/invalid-range-literal-start-after-end.jv @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype TestProperty { + textProperty: range A2:B1; + } +} diff --git a/libs/language-server/src/test/assets/range-literal/valid-range-literal-unlimited-range.jv b/libs/language-server/src/test/assets/range-literal/valid-range-literal-unlimited-range.jv new file mode 100644 index 00000000..02b36ec9 --- /dev/null +++ b/libs/language-server/src/test/assets/range-literal/valid-range-literal-unlimited-range.jv @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype TestProperty { + textProperty: range **:**; + } +} diff --git a/libs/language-server/src/test/assets/range-literal/valid-range-literal.jv b/libs/language-server/src/test/assets/range-literal/valid-range-literal.jv new file mode 100644 index 00000000..8d4a3f9f --- /dev/null +++ b/libs/language-server/src/test/assets/range-literal/valid-range-literal.jv @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype TestProperty { + textProperty: range A1:A2; + } +} diff --git a/libs/language-server/src/test/assets/regex-literal/invalid-regex-literal-syntax-error.jv b/libs/language-server/src/test/assets/regex-literal/invalid-regex-literal-syntax-error.jv new file mode 100644 index 00000000..f629c060 --- /dev/null +++ b/libs/language-server/src/test/assets/regex-literal/invalid-regex-literal-syntax-error.jv @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype TestProperty { + textProperty: /a[+/; + } +} diff --git a/libs/language-server/src/test/assets/regex-literal/valid-regex-literal.jv b/libs/language-server/src/test/assets/regex-literal/valid-regex-literal.jv new file mode 100644 index 00000000..dd387f01 --- /dev/null +++ b/libs/language-server/src/test/assets/regex-literal/valid-regex-literal.jv @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype TestProperty { + textProperty: /a+/; + } +} diff --git a/libs/language-server/src/test/assets/transform-body/invalid-duplicate-ports.jv b/libs/language-server/src/test/assets/transform-body/invalid-duplicate-ports.jv new file mode 100644 index 00000000..f376eeb1 --- /dev/null +++ b/libs/language-server/src/test/assets/transform-body/invalid-duplicate-ports.jv @@ -0,0 +1,10 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +transform Transform { + from inputParam oftype decimal; + to inputParam oftype integer; + + inputParam: floor(inputParam); +} diff --git a/libs/language-server/src/test/assets/transform-body/invalid-missing-output-assignment.jv b/libs/language-server/src/test/assets/transform-body/invalid-missing-output-assignment.jv new file mode 100644 index 00000000..97bfba48 --- /dev/null +++ b/libs/language-server/src/test/assets/transform-body/invalid-missing-output-assignment.jv @@ -0,0 +1,8 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +transform Transform { + from inputParam oftype decimal; + to result oftype integer; +} diff --git a/libs/language-server/src/test/assets/transform-body/invalid-missing-output-port.jv b/libs/language-server/src/test/assets/transform-body/invalid-missing-output-port.jv new file mode 100644 index 00000000..2cd9e61b --- /dev/null +++ b/libs/language-server/src/test/assets/transform-body/invalid-missing-output-port.jv @@ -0,0 +1,7 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +transform Transform { + from inputParam oftype decimal; +} diff --git a/libs/language-server/src/test/assets/transform-body/invalid-multiple-output-assignments.jv b/libs/language-server/src/test/assets/transform-body/invalid-multiple-output-assignments.jv new file mode 100644 index 00000000..e8f7ab57 --- /dev/null +++ b/libs/language-server/src/test/assets/transform-body/invalid-multiple-output-assignments.jv @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +transform Transform { + from inputParam oftype decimal; + to result oftype integer; + + result: floor(inputParam); + result: ceil(inputParam); +} diff --git a/libs/language-server/src/test/assets/transform-body/invalid-multiple-output-ports.jv b/libs/language-server/src/test/assets/transform-body/invalid-multiple-output-ports.jv new file mode 100644 index 00000000..aedaa7ab --- /dev/null +++ b/libs/language-server/src/test/assets/transform-body/invalid-multiple-output-ports.jv @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +transform Transform { + from inputParam oftype decimal; + to result oftype integer; + to secondOutput oftype integer; + + result: floor(inputParam); + secondOutput: 2; +} diff --git a/libs/language-server/src/test/assets/transform-body/valid-missing-input-port.jv b/libs/language-server/src/test/assets/transform-body/valid-missing-input-port.jv new file mode 100644 index 00000000..f9122846 --- /dev/null +++ b/libs/language-server/src/test/assets/transform-body/valid-missing-input-port.jv @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +transform Transform { + to result oftype integer; + + result: floor(inputParam); +} diff --git a/libs/language-server/src/test/assets/transform-body/valid-multiple-input-ports.jv b/libs/language-server/src/test/assets/transform-body/valid-multiple-input-ports.jv new file mode 100644 index 00000000..83442ac8 --- /dev/null +++ b/libs/language-server/src/test/assets/transform-body/valid-multiple-input-ports.jv @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +transform Transform { + from inputParam oftype decimal; + from secondInput oftype decimal; + to result oftype integer; + + result: floor(inputParam); +} diff --git a/libs/language-server/src/test/assets/transform-body/valid-transform-body.jv b/libs/language-server/src/test/assets/transform-body/valid-transform-body.jv new file mode 100644 index 00000000..5bd12907 --- /dev/null +++ b/libs/language-server/src/test/assets/transform-body/valid-transform-body.jv @@ -0,0 +1,10 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +transform Transform { + from inputParam oftype decimal; + to result oftype integer; + + result: floor(inputParam); +} diff --git a/libs/language-server/src/test/assets/transform-output-assignment/invalid-invalid-type.jv b/libs/language-server/src/test/assets/transform-output-assignment/invalid-invalid-type.jv new file mode 100644 index 00000000..c42c8139 --- /dev/null +++ b/libs/language-server/src/test/assets/transform-output-assignment/invalid-invalid-type.jv @@ -0,0 +1,10 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +transform Transform { + from inputParam oftype decimal; + to result oftype integer; + + result: inputParam * 1.5; +} diff --git a/libs/language-server/src/test/assets/transform-output-assignment/invalid-output-port-in-assignment.jv b/libs/language-server/src/test/assets/transform-output-assignment/invalid-output-port-in-assignment.jv new file mode 100644 index 00000000..7e2b4446 --- /dev/null +++ b/libs/language-server/src/test/assets/transform-output-assignment/invalid-output-port-in-assignment.jv @@ -0,0 +1,10 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +transform Transform { + from inputParam oftype decimal; + to result oftype integer; + + result: result; +} diff --git a/libs/language-server/src/test/assets/transform-output-assignment/valid-transform-output-assignment.jv b/libs/language-server/src/test/assets/transform-output-assignment/valid-transform-output-assignment.jv new file mode 100644 index 00000000..5bd12907 --- /dev/null +++ b/libs/language-server/src/test/assets/transform-output-assignment/valid-transform-output-assignment.jv @@ -0,0 +1,10 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +transform Transform { + from inputParam oftype decimal; + to result oftype integer; + + result: floor(inputParam); +} diff --git a/libs/language-server/src/test/assets/typed-constraint-definition/invalid-unknown-constraint-type.jv b/libs/language-server/src/test/assets/typed-constraint-definition/invalid-unknown-constraint-type.jv new file mode 100644 index 00000000..964f1255 --- /dev/null +++ b/libs/language-server/src/test/assets/typed-constraint-definition/invalid-unknown-constraint-type.jv @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +constraint Constraint oftype UnknownConstraint { +} diff --git a/libs/language-server/src/test/assets/typed-constraint-definition/valid-typed-constraint.jv b/libs/language-server/src/test/assets/typed-constraint-definition/valid-typed-constraint.jv new file mode 100644 index 00000000..fef0f787 --- /dev/null +++ b/libs/language-server/src/test/assets/typed-constraint-definition/valid-typed-constraint.jv @@ -0,0 +1,7 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +constraint Constraint oftype LengthConstraint { + maxLength: 10; +} diff --git a/libs/language-server/src/test/assets/validation-utils/invalid-duplicate-property-names.jv b/libs/language-server/src/test/assets/validation-utils/invalid-duplicate-property-names.jv new file mode 100644 index 00000000..b6ba2765 --- /dev/null +++ b/libs/language-server/src/test/assets/validation-utils/invalid-duplicate-property-names.jv @@ -0,0 +1,10 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype TestProperty { + textProperty: ''; + textProperty: ''; + } +} diff --git a/libs/language-server/src/test/assets/validation-utils/valid-distinct-property-names.jv b/libs/language-server/src/test/assets/validation-utils/valid-distinct-property-names.jv new file mode 100644 index 00000000..fc7a3c5a --- /dev/null +++ b/libs/language-server/src/test/assets/validation-utils/valid-distinct-property-names.jv @@ -0,0 +1,10 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block Test oftype TestProperty { + textProperty: ''; + textProperties: ''; + } +} diff --git a/libs/language-server/src/test/assets/value-type-definition/invalid-duplicate-generic.jv b/libs/language-server/src/test/assets/value-type-definition/invalid-duplicate-generic.jv new file mode 100644 index 00000000..ea916ab3 --- /dev/null +++ b/libs/language-server/src/test/assets/value-type-definition/invalid-duplicate-generic.jv @@ -0,0 +1,7 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +valuetype ValueType<T, T> oftype text { + constraints: []; +} diff --git a/libs/language-server/src/test/assets/value-type-definition/invalid-invalid-constraint-type-for-value-type.jv b/libs/language-server/src/test/assets/value-type-definition/invalid-invalid-constraint-type-for-value-type.jv new file mode 100644 index 00000000..d5a21ac3 --- /dev/null +++ b/libs/language-server/src/test/assets/value-type-definition/invalid-invalid-constraint-type-for-value-type.jv @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +constraint Constraint on integer: + value > 10; + +valuetype ValueType oftype text { + constraints: [ + Constraint + ]; +} diff --git a/libs/language-server/src/test/assets/value-type-definition/invalid-invalid-constraints-item.jv b/libs/language-server/src/test/assets/value-type-definition/invalid-invalid-constraints-item.jv new file mode 100644 index 00000000..fe12170c --- /dev/null +++ b/libs/language-server/src/test/assets/value-type-definition/invalid-invalid-constraints-item.jv @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +valuetype ValueType oftype text { + constraints: [ + 10 > 9 + ]; +} diff --git a/libs/language-server/src/test/assets/value-type-definition/invalid-missing-builtin-keyword.jv b/libs/language-server/src/test/assets/value-type-definition/invalid-missing-builtin-keyword.jv new file mode 100644 index 00000000..8bf4aded --- /dev/null +++ b/libs/language-server/src/test/assets/value-type-definition/invalid-missing-builtin-keyword.jv @@ -0,0 +1,5 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +valuetype ValueType; diff --git a/libs/language-server/src/test/assets/value-type-definition/invalid-missing-generic.jv b/libs/language-server/src/test/assets/value-type-definition/invalid-missing-generic.jv new file mode 100644 index 00000000..fe75472d --- /dev/null +++ b/libs/language-server/src/test/assets/value-type-definition/invalid-missing-generic.jv @@ -0,0 +1,7 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +valuetype ValueType<> oftype text { + constraints: []; +}; diff --git a/libs/language-server/src/test/assets/value-type-definition/invalid-supertype-cycle.jv b/libs/language-server/src/test/assets/value-type-definition/invalid-supertype-cycle.jv new file mode 100644 index 00000000..ca7b8e49 --- /dev/null +++ b/libs/language-server/src/test/assets/value-type-definition/invalid-supertype-cycle.jv @@ -0,0 +1,7 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +valuetype ValueType oftype ValueType { + constraints: []; +} diff --git a/libs/language-server/src/test/assets/value-type-definition/invalid-unallowed-builtin-body.jv b/libs/language-server/src/test/assets/value-type-definition/invalid-unallowed-builtin-body.jv new file mode 100644 index 00000000..708235e4 --- /dev/null +++ b/libs/language-server/src/test/assets/value-type-definition/invalid-unallowed-builtin-body.jv @@ -0,0 +1,7 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +builtin valuetype ValueType { + constraints: []; +}; diff --git a/libs/language-server/src/test/assets/value-type-definition/valid-builtin-value-type-generic.jv b/libs/language-server/src/test/assets/value-type-definition/valid-builtin-value-type-generic.jv new file mode 100644 index 00000000..b0204306 --- /dev/null +++ b/libs/language-server/src/test/assets/value-type-definition/valid-builtin-value-type-generic.jv @@ -0,0 +1,5 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +builtin valuetype ValueType<MyInnerType1, MyInnerType2>; diff --git a/libs/language-server/src/test/assets/value-type-definition/valid-value-type-definition.jv b/libs/language-server/src/test/assets/value-type-definition/valid-value-type-definition.jv new file mode 100644 index 00000000..1e2d884c --- /dev/null +++ b/libs/language-server/src/test/assets/value-type-definition/valid-value-type-definition.jv @@ -0,0 +1,7 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +valuetype ValueType oftype text { + constraints: []; +} diff --git a/libs/language-server/src/test/assets/value-type-reference/invalid-reference-missing-generic.jv b/libs/language-server/src/test/assets/value-type-reference/invalid-reference-missing-generic.jv new file mode 100644 index 00000000..929dbda3 --- /dev/null +++ b/libs/language-server/src/test/assets/value-type-reference/invalid-reference-missing-generic.jv @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +valuetype ValueType <S, T> oftype text { + constraints: []; +} + +valuetype Ref oftype ValueType { + constraints: []; +} diff --git a/libs/language-server/src/test/assets/value-type-reference/invalid-reference-to-non-referenceable-value-type-in-block.jv b/libs/language-server/src/test/assets/value-type-reference/invalid-reference-to-non-referenceable-value-type-in-block.jv new file mode 100644 index 00000000..4b6b2378 --- /dev/null +++ b/libs/language-server/src/test/assets/value-type-reference/invalid-reference-to-non-referenceable-value-type-in-block.jv @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline Pipeline { + block TestBlock oftype TableInterpreter { + columns: [ + "tesCol" oftype Constraint, + ]; + } +} diff --git a/libs/language-server/src/test/assets/value-type-reference/invalid-reference-to-non-referenceable-value-type-in-value-type.jv b/libs/language-server/src/test/assets/value-type-reference/invalid-reference-to-non-referenceable-value-type-in-value-type.jv new file mode 100644 index 00000000..085fac3a --- /dev/null +++ b/libs/language-server/src/test/assets/value-type-reference/invalid-reference-to-non-referenceable-value-type-in-value-type.jv @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +valuetype ValueType1 oftype Constraint { + constraints: []; +} + +valuetype ValueType2 oftype Regex { + constraints: []; +} diff --git a/libs/language-server/src/test/assets/value-type-reference/invalid-reference-too-few-generic-parameters.jv b/libs/language-server/src/test/assets/value-type-reference/invalid-reference-too-few-generic-parameters.jv new file mode 100644 index 00000000..de9ce7a2 --- /dev/null +++ b/libs/language-server/src/test/assets/value-type-reference/invalid-reference-too-few-generic-parameters.jv @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +valuetype ValueType <S, T> oftype text { + constraints: []; +} + +valuetype Ref oftype ValueType<text> { + constraints: []; +} diff --git a/libs/language-server/src/test/assets/value-type-reference/invalid-reference-too-many-generic-parameters.jv b/libs/language-server/src/test/assets/value-type-reference/invalid-reference-too-many-generic-parameters.jv new file mode 100644 index 00000000..c1823168 --- /dev/null +++ b/libs/language-server/src/test/assets/value-type-reference/invalid-reference-too-many-generic-parameters.jv @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +valuetype ValueType <S, T> oftype text { + constraints: []; +} + +valuetype Ref oftype ValueType<text, integer, text> { + constraints: []; +} diff --git a/libs/language-server/src/test/assets/value-type-reference/invalid-reference-too-non-generic-with-generic-parameters.jv b/libs/language-server/src/test/assets/value-type-reference/invalid-reference-too-non-generic-with-generic-parameters.jv new file mode 100644 index 00000000..52199d59 --- /dev/null +++ b/libs/language-server/src/test/assets/value-type-reference/invalid-reference-too-non-generic-with-generic-parameters.jv @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +valuetype ValueType oftype text { + constraints: []; +} + +valuetype Ref oftype ValueType<text> { + constraints: []; +} diff --git a/libs/language-server/src/test/assets/value-type-reference/valid-reference-to-multiple-generic-value-type.jv b/libs/language-server/src/test/assets/value-type-reference/valid-reference-to-multiple-generic-value-type.jv new file mode 100644 index 00000000..b345e2d1 --- /dev/null +++ b/libs/language-server/src/test/assets/value-type-reference/valid-reference-to-multiple-generic-value-type.jv @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +valuetype ValueType <S, T, R, Q, P> oftype text { + constraints: []; +} + +valuetype Ref oftype ValueType<text, integer, decimal, text, text> { + constraints: []; +} diff --git a/libs/language-server/src/test/assets/value-type-reference/valid-reference-to-non-generic-value-type.jv b/libs/language-server/src/test/assets/value-type-reference/valid-reference-to-non-generic-value-type.jv new file mode 100644 index 00000000..5e1cf107 --- /dev/null +++ b/libs/language-server/src/test/assets/value-type-reference/valid-reference-to-non-generic-value-type.jv @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +valuetype ValueType oftype text { + constraints: []; +} + +valuetype Ref oftype ValueType { + constraints: []; +} diff --git a/libs/language-server/src/test/assets/value-type-reference/valid-reference-to-non-referenceable-value-type-in-builtin-block-type.jv b/libs/language-server/src/test/assets/value-type-reference/valid-reference-to-non-referenceable-value-type-in-builtin-block-type.jv new file mode 100644 index 00000000..fbc52459 --- /dev/null +++ b/libs/language-server/src/test/assets/value-type-reference/valid-reference-to-non-referenceable-value-type-in-builtin-block-type.jv @@ -0,0 +1,7 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +builtin blocktype MyBlockType { + property x oftype Constraint; +} diff --git a/libs/language-server/src/test/assets/value-type-reference/valid-reference-to-single-generic-value-type.jv b/libs/language-server/src/test/assets/value-type-reference/valid-reference-to-single-generic-value-type.jv new file mode 100644 index 00000000..0a2dcce2 --- /dev/null +++ b/libs/language-server/src/test/assets/value-type-reference/valid-reference-to-single-generic-value-type.jv @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +valuetype ValueType<T> oftype text { + constraints: []; +} + +valuetype Ref oftype ValueType<text> { + constraints: []; +} diff --git a/libs/language-server/src/test/index.ts b/libs/language-server/src/test/index.ts new file mode 100644 index 00000000..fd181e69 --- /dev/null +++ b/libs/language-server/src/test/index.ts @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +export * from './langium-utils'; +export * from './utils'; diff --git a/libs/language-server/src/test/langium-utils.ts b/libs/language-server/src/test/langium-utils.ts new file mode 100644 index 00000000..1b2aa32d --- /dev/null +++ b/libs/language-server/src/test/langium-utils.ts @@ -0,0 +1,59 @@ +// SPDX-FileCopyrightText: 2021 TypeFox GmbH +// +// SPDX-License-Identifier: MIT +// +/** + * The content of this file was copied from the official langium github repo + * https://github.com/langium/langium/blob/main/packages/langium/src/test/langium-test.ts + */ +import { type AstNode, type BuildOptions, type LangiumDocument } from 'langium'; +import { type LangiumServices } from 'langium/lsp'; +import { type Diagnostic } from 'vscode-languageserver'; +import { URI } from 'vscode-uri'; + +import { initializeWorkspace } from '../lib/builtin-library/jayvee-workspace-manager'; + +export interface ParseHelperOptions extends BuildOptions { + documentUri?: string; +} + +export function parseHelper<T extends AstNode = AstNode>( + services: LangiumServices, +): ( + input: string, + options?: ParseHelperOptions, +) => Promise<LangiumDocument<T>> { + const metaData = services.LanguageMetaData; + const documentBuilder = services.shared.workspace.DocumentBuilder; + return async (input, options) => { + const randomNumber = Math.floor(Math.random() * 10000000) + 1000000; + const uri = URI.parse( + options?.documentUri ?? + `file:///${randomNumber}${metaData.fileExtensions[0] ?? ''}`, + ); + const document = + services.shared.workspace.LangiumDocumentFactory.fromString<T>( + input, + uri, + ); + services.shared.workspace.LangiumDocuments.addDocument(document); + await initializeWorkspace(services); + await documentBuilder.build([document], options); + return document; + }; +} + +export interface ValidationResult<T extends AstNode = AstNode> { + diagnostics: Diagnostic[]; + document: LangiumDocument<T>; +} + +export function validationHelper<T extends AstNode = AstNode>( + services: LangiumServices, +): (input: string) => Promise<ValidationResult<T>> { + const parse = parseHelper<T>(services); + return async (input) => { + const document = await parse(input, { validation: true }); + return { document, diagnostics: document.diagnostics ?? [] }; + }; +} diff --git a/libs/language-server/src/test/utils.ts b/libs/language-server/src/test/utils.ts new file mode 100644 index 00000000..faa868c1 --- /dev/null +++ b/libs/language-server/src/test/utils.ts @@ -0,0 +1,94 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// eslint-disable-next-line unicorn/prefer-node-protocol +import { strict as assert } from 'assert'; +import { readFileSync } from 'node:fs'; +import path from 'node:path'; + +import { + type AstNode, + type LangiumDocument, + type ValidationAcceptor, +} from 'langium'; +import { type WorkspaceFolder } from 'vscode-languageserver-protocol'; + +import { + EvaluationContext, + type JayveeServices, + type JayveeValidationProps, + ValidationContext, +} from '../lib'; +import { initializeWorkspace } from '../lib/builtin-library/jayvee-workspace-manager'; + +// eslint-disable-next-line @typescript-eslint/no-empty-function +export const validationAcceptorMockImpl: ValidationAcceptor = () => {}; + +/** + * Returns function for reading a jv test asset file from the specified asset root + * @param assetPath paths to the asset root directory + * @returns function for reading jv asset file + */ +export function readJvTestAssetHelper( + ...assetPath: string[] +): (testFileName: string) => string { + /** + * Reads the jv test asset file with the given filename from the previously configured asset directory + * @param testFileName asset filename containing jv code + * @returns content of asset file + */ + return (testFileName: string) => { + const text = readFileSync( + path.resolve(...assetPath, testFileName), + 'utf-8', + ); + // Expect the test asset to contain something + expect(text).not.toBe(''); + return text; + }; +} + +export function expectNoParserAndLexerErrors( + document: LangiumDocument<AstNode>, +) { + expect(document.parseResult.parserErrors).toHaveLength(0); + expect(document.parseResult.lexerErrors).toHaveLength(0); +} + +export async function loadTestExtensions( + services: JayveeServices, + testExtensionJvFiles: string[], +) { + assert(testExtensionJvFiles.every((file) => file.endsWith('.jv'))); + const extensions: WorkspaceFolder[] = testExtensionJvFiles.map((file) => ({ + uri: path.dirname(file), + name: path.basename(file), + })); + return initializeWorkspace(services, extensions); +} + +export function createJayveeValidationProps( + validationAcceptor: ValidationAcceptor, + services: JayveeServices, +): JayveeValidationProps { + const valueTypeProvider = services.ValueTypeProvider; + const operatorEvaluatorRegistry = services.operators.EvaluatorRegistry; + const wrapperFactories = services.WrapperFactories; + const operatorTypeComputerRegistry = services.operators.TypeComputerRegistry; + const runtimeParameterProvider = services.RuntimeParameterProvider; + + return { + validationContext: new ValidationContext( + validationAcceptor, + operatorTypeComputerRegistry, + ), + evaluationContext: new EvaluationContext( + runtimeParameterProvider, + operatorEvaluatorRegistry, + valueTypeProvider, + ), + valueTypeProvider: valueTypeProvider, + wrapperFactories: wrapperFactories, + }; +} diff --git a/libs/language-server/tsconfig.json b/libs/language-server/tsconfig.json new file mode 100644 index 00000000..62ebbd94 --- /dev/null +++ b/libs/language-server/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../tsconfig.base.json", + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/libs/language-server/tsconfig.json.license b/libs/language-server/tsconfig.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/language-server/tsconfig.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/language-server/tsconfig.lib.json b/libs/language-server/tsconfig.lib.json new file mode 100644 index 00000000..39d7077a --- /dev/null +++ b/libs/language-server/tsconfig.lib.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "declaration": true, + "types": ["node"], + }, + "include": ["**/*.ts"], + "exclude": ["vite.config.ts", "**/*.spec.ts", "**/*.test.ts", "./src/test/**/*.ts"] +} diff --git a/libs/language-server/tsconfig.lib.json.license b/libs/language-server/tsconfig.lib.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/language-server/tsconfig.lib.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/language-server/tsconfig.spec.json b/libs/language-server/tsconfig.spec.json new file mode 100644 index 00000000..3c002c21 --- /dev/null +++ b/libs/language-server/tsconfig.spec.json @@ -0,0 +1,26 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "types": [ + "vitest/globals", + "vitest/importMeta", + "vite/client", + "node", + "vitest" + ] + }, + "include": [ + "vite.config.ts", + "vitest.config.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.test.tsx", + "src/**/*.spec.tsx", + "src/**/*.test.js", + "src/**/*.spec.js", + "src/**/*.test.jsx", + "src/**/*.spec.jsx", + "src/**/*.d.ts" + ] +} diff --git a/libs/language-server/tsconfig.spec.json.license b/libs/language-server/tsconfig.spec.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/language-server/tsconfig.spec.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/language-server/vite.config.ts b/libs/language-server/vite.config.ts new file mode 100644 index 00000000..43670698 --- /dev/null +++ b/libs/language-server/vite.config.ts @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +// / <reference types='vitest' /> +import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + root: __dirname, + cacheDir: '../../node_modules/.vite/libs/language-server', + + plugins: [nxViteTsPaths()], + + test: { + globals: true, + cacheDir: '../../node_modules/.vitest', + environment: 'node', + include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], + + reporters: ['default'], + coverage: { + reportsDirectory: '../../coverage/libs/language-server', + provider: 'v8', + }, + }, +}); diff --git a/libs/monaco-editor/.babelrc b/libs/monaco-editor/.babelrc new file mode 100644 index 00000000..1ea870ea --- /dev/null +++ b/libs/monaco-editor/.babelrc @@ -0,0 +1,12 @@ +{ + "presets": [ + [ + "@nx/react/babel", + { + "runtime": "automatic", + "useBuiltIns": "usage" + } + ] + ], + "plugins": [] +} diff --git a/libs/monaco-editor/.babelrc.license b/libs/monaco-editor/.babelrc.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/monaco-editor/.babelrc.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/monaco-editor/.eslintrc.js b/libs/monaco-editor/.eslintrc.js new file mode 100644 index 00000000..165ee4c5 --- /dev/null +++ b/libs/monaco-editor/.eslintrc.js @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +module.exports = { + extends: ['plugin:@nx/react', '../../.eslintrc.json'], + ignorePatterns: ['!**/*', '/*.*', 'src/lib/*.monarch.ts'], + parserOptions: { + project: ['./tsconfig.lib.json'], + tsconfigRootDir: __dirname, + }, + overrides: [ + { + files: ['*.ts', '*.tsx', '*.js', '*.jsx'], + rules: {}, + }, + { + files: ['*.ts', '*.tsx'], + rules: { + 'import/no-unresolved': 'off', + 'react/jsx-no-useless-fragment': 'off', + }, + }, + { + files: ['*.js', '*.jsx'], + rules: {}, + }, + ], +}; diff --git a/libs/monaco-editor/.gitignore b/libs/monaco-editor/.gitignore new file mode 100644 index 00000000..4169fd36 --- /dev/null +++ b/libs/monaco-editor/.gitignore @@ -0,0 +1,6 @@ +# SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +# +# SPDX-License-Identifier: AGPL-3.0-only + +# file generated by Langium +src/lib/jayvee.monarch.ts diff --git a/libs/monaco-editor/README.md b/libs/monaco-editor/README.md new file mode 100644 index 00000000..5e60ec66 --- /dev/null +++ b/libs/monaco-editor/README.md @@ -0,0 +1,197 @@ +<!-- +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only +--> + +# @jvalue/monaco-editor + +This library contains a React component that will spawn an instance of the Monaco Editor. This instance can only be used to edit Jayvee language files. + +## How to use the component + +Install both the monaco editor and the language server via `npm`: + +```bash +npm install @jvalue/monaco-editor @jvalue/language-server-web-worker +``` + +Enable Web Workers in your React project as the language server will run in its own Web Worker. +Open `tsconfig.json` and add "WebWorker" to the lib array: + +```diff +{ + "compilerOptions": { +- "lib": ["dom", "dom.iterable", "esnext"], ++ "lib": ["dom", "dom.iterable", "esnext", "WebWorker"], + } +} +``` + +The following sample code spawns an instance of the monaco editor and runs the corresponding +language server in a separate Web Worker: + +```tsx +import { MonacoEditor } from '@jvalue/jayvee-monaco'; +import React from 'react'; + +function startJayveeWorker(): Worker { + const worker = new Worker( + // TODO adjust the path / URL to the language server: + new URL('<path-to-language-server-web-worker>/main.js', import.meta.url), { + type: 'module', + }); + return worker; +} +export const EditorExample: React.FC = () => { + return ( + <div style={{ height: '500px' }}> + <MonacoEditor + startJayveeWorker={startJayveeWorker} + editorText={'Add example code here'} + onDidChangeEditorText={(newText): void => console.log(newText)} + /> + </div> + ); +}; +``` + +In case you bundle your React app with webpack, you are able to use a hard-coded relative path to the `main.js` file +included in `@jvalue/language-server-web-worker`. +Such a path looks like `../../node_modules/@jvalue/language-server-web-worker/main.js` but probably needs +slight adjustments, so it navigates to the desired file location. + +Alternatively, you can host the `main.js` file of `@jvalue/language-server-web-worker` and make it available under a +certain URL. +Then, you can use that URL for instantiating the Web Worker instead of the previously mentioned file path. + +## Development set-up + +> The following guide explains how to install this component without relying on a npm registry or the like. This is particularly useful to manually test the component locally before publishing it. + +### Prerequisites + +We expect the component to be used in a (default) Create-React-App project. This project should ideally be created outside of the directory where the Jayvee repository is stored. + +### Build the language server Web Worker + +The [`language-server-web-worker`](../../apps/language-server-web-worker) project provides a ready-to-use language server to be run in a Web +Worker. +It is required by the monaco editor in order to provide features like autocompletion and code diagnostics. +Build the project using + +```bash +npx nx build language-server-web-worker +``` + +This creates a file `main.js` in the folder `<jayvee project root>/dist/apps/language-server-web-worker` which is +required in a later step. + +### Building and packing the monaco editor + +To build and pack the monaco editor, navigate to the project root folder and run + +```bash +npx nx pack @jvalue/monaco-editor +``` + +Now, in the directory `<jayvee project +root>/dist/libs/monaco-editor`, you will find a file named `monaco-editor-<version>.tgz` +with `<version>` being the version of the monaco editor package. + +We want to be able to use the package in another project. To make the package behave as if it was directly pulled +from a registry, we first want to pack it. To do this, run `npm pack` in the directory `<jayvee project +root>/dist/libs/monaco-editor`. + +Now, you will find a file named `jvalue-monaco-editor-<version>.tgz` in that directory, +with `<version>` being the version of the monaco editor package. + +### Installation of the monaco editor + +We want to be able to use the package in the React project as if it was directly pulled +from a registry. To achieve that, switch to the React project where you want to test the editor. Open `package.json` +and add the following dependency: + +``` + "dependencies": { + "@jvalue/jayvee-monaco": "file:../../../jayvee/dist/libs/monaco-editor/jvalue-monaco-editor-0.0.0.tgz" + }, +``` + +You might have to adjust the paths to the files (and especially the version at the end). + +Now, run `npm install`. + +> Tip: Whenever you create a new version of the packages using `npm pack`, you have to install them again. This sometimes results in errors regarding the package integrity, because the two packages will contain new content, leading to a new hash. A fairly easy way to deal with this is to uninstall the two packages (i.e. to remove them from the dependencies and to call `npm install`), and then to re-install them. If you need to do this frequently, you could also symlink the packages directly, but this might require you to install the peer dependencies manually. Another important point: CreateReactApp seems to cache the build output a bit too aggressively in some cases. After installing a new version of the package, you might need to delete `node_modules/.cache/`. + +### Enable Web Workers + +Furthermore, we need to enable Web Workers in the React project. Open `tsconfig.json` and add "WebWorker" to the +lib array: + +```diff + +{ + "compilerOptions": { +- "lib": ["dom", "dom.iterable", "esnext"], ++ "lib": ["dom", "dom.iterable", "esnext", "WebWorker"], + } +} + +``` + +### Create the actual editor + +Now, create a new file `my-editor.tsx` where the actual editor will be contained. Add the following file content: + +```tsx +import { MonacoEditor } from '@jvalue/jayvee-monaco'; +import React from 'react'; + +const exampleCode = 'Add example code here'; + +function startJayveeWorker(): Worker { + // TODO adjust the path to the language server: + const worker = new Worker(new URL('<jayvee project root>/dist/apps/language-server-web-worker/main.js', import.meta. + url), { + type: 'module', + }); + + return worker; +} +export const EditorExample: React.FC = () => { + return ( + <div style={{ height: '500px' }}> + <MonacoEditor + startJayveeWorker={startJayveeWorker} + editorText={exampleCode} + onDidChangeEditorText={(newText): void => console.log(newText)} + /> + </div> + ); +}; +``` + +> Make sure to adjust the path of the URL so it refers to the `main.js` file of the built language server +> created earlier. + +That's it! You can now use the editor in your React project. + +#### Source Maps warnings + +When compiling your React project, you might get a large number of Webpack warnings, saying that Source Maps from `vscode-languageserver` cannot be parsed. It is not clear why this is happening. Maybe, this is related to https://github.com/microsoft/vscode-languageserver-node/issues/879 and https://github.com/microsoft/vscode-languageserver-node/pull/908 (even though these issues seem to be resolved). + +## Development notes + +### Webpack URLs + +When using `new URL('something')`, Webpack uses static analysis to bundle the referenced item. So this is not just +runtime code - it is used during compilation. It was mentioned somewhere in the long discussion here: +https://github.com/webpack/webpack/discussions/13655#discussioncomment-937300. Also refer to the corresponding [webpack documentation](https://webpack.js.org/guides/web-workers/). + +### Resources + +The following resources were pretty useful when building the component: + +- https://github.com/eclipse-theia/theia/wiki/LSP-and-Monaco-Integration +- https://github.com/TypeFox/monaco-languageclient (includes an example regarding the in-browser LSP, see https://github.com/TypeFox/monaco-languageclient/blob/c5511b19e95e237c3f95a0fc0588769263f3ba40/packages/examples/browser-lsp/src) diff --git a/libs/monaco-editor/package.json b/libs/monaco-editor/package.json new file mode 100644 index 00000000..853160c7 --- /dev/null +++ b/libs/monaco-editor/package.json @@ -0,0 +1,16 @@ +{ + "name": "@jvalue/jayvee-monaco", + "main": "./index.js", + "bugs": { + "url": "https://github.com/jvalue/jayvee/issues" + }, + "repository": { + "type": "git", + "url": "https://github.com/jvalue/jayvee.git" + }, + "homepage": "https://github.com/jvalue/jayvee", + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org" + } +} diff --git a/libs/monaco-editor/package.json.license b/libs/monaco-editor/package.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/monaco-editor/package.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/monaco-editor/project.json b/libs/monaco-editor/project.json new file mode 100644 index 00000000..07c5964f --- /dev/null +++ b/libs/monaco-editor/project.json @@ -0,0 +1,71 @@ +{ + "name": "monaco-editor", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/monaco-editor/src", + "projectType": "library", + "tags": [], + "targets": { + "lint": { + "executor": "@nx/eslint:lint", + "outputs": ["{options.outputFile}"] + }, + "build": { + "executor": "@nx/rollup:rollup", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/libs/monaco-editor", + "tsConfig": "libs/monaco-editor/tsconfig.lib.json", + "project": "libs/monaco-editor/package.json", + "entryFile": "libs/monaco-editor/src/index.ts", + "external": ["react/jsx-runtime"], + "rollupConfig": "@nx/react/plugins/bundle-rollup", + "compiler": "babel", + "assets": [ + { + "glob": "libs/monaco-editor/README.md", + "input": ".", + "output": "." + } + ], + "babelUpwardRootMode": true, + "updateBuildableProjectDepsInPackageJson": true + }, + "configurations": { + "dev": {}, + "prod": {} + }, + "defaultConfiguration": "dev" + }, + "pre-publish": { + "executor": "nx:run-commands", + "dependsOn": ["build"], + "options": { + "commands": [ + "node tools/scripts/relax-peer-dependency-versions.mjs monaco-editor", + "node tools/scripts/monaco-editor/delete-vscode-peer-dependency.mjs monaco-editor", + "node tools/scripts/monaco-editor/relax-react-version.mjs monaco-editor", + "node tools/scripts/add-package-json-version.mjs monaco-editor", + "node tools/scripts/publish.mjs monaco-editor false" // dry-run + ], + "parallel": false + } + }, + "publish": { + "executor": "nx:run-commands", + "dependsOn": ["pre-publish"], + "options": { + "commands": ["node tools/scripts/publish.mjs monaco-editor true"], + "parallel": false + } + }, + "pack": { + "executor": "nx:run-commands", + "dependsOn": ["pre-publish"], + "options": { + "commands": ["node tools/scripts/pack.mjs monaco-editor"], + "parallel": false + } + } + }, + "implicitDependencies": ["language-server"] +} diff --git a/libs/monaco-editor/project.json.license b/libs/monaco-editor/project.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/monaco-editor/project.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/monaco-editor/src/index.ts b/libs/monaco-editor/src/index.ts new file mode 100644 index 00000000..5dd643bb --- /dev/null +++ b/libs/monaco-editor/src/index.ts @@ -0,0 +1,5 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +export * from './lib/monaco-editor'; diff --git a/libs/monaco-editor/src/lib/monaco-editor.tsx b/libs/monaco-editor/src/lib/monaco-editor.tsx new file mode 100644 index 00000000..39ba8d2e --- /dev/null +++ b/libs/monaco-editor/src/lib/monaco-editor.tsx @@ -0,0 +1,257 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +/* + The necessary imports for the Monaco editor can be found here: + https://github.com/TypeFox/monaco-languageclient/blob/c5511b19e95e237c3f95a0fc0588769263f3ba40/packages/examples/browser-lsp/src/client.ts +*/ + +import 'monaco-editor/esm/vs/editor/editor.all.js'; +import * as monaco from 'monaco-editor/esm/vs/editor/editor.api.js'; +import 'monaco-editor/esm/vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.js'; +import 'monaco-editor/esm/vs/editor/standalone/browser/inspectTokens/inspectTokens.js'; +import 'monaco-editor/esm/vs/editor/standalone/browser/iPadShowKeyboard/iPadShowKeyboard.js'; +import 'monaco-editor/esm/vs/editor/standalone/browser/quickAccess/standaloneCommandsQuickAccess.js'; +import 'monaco-editor/esm/vs/editor/standalone/browser/quickAccess/standaloneGotoLineQuickAccess.js'; +import 'monaco-editor/esm/vs/editor/standalone/browser/quickAccess/standaloneGotoSymbolQuickAccess.js'; +import 'monaco-editor/esm/vs/editor/standalone/browser/quickAccess/standaloneHelpQuickAccess.js'; +import 'monaco-editor/esm/vs/editor/standalone/browser/quickInput/standaloneQuickInputService.js'; +import 'monaco-editor/esm/vs/editor/standalone/browser/referenceSearch/standaloneReferenceSearch.js'; +import 'monaco-editor/esm/vs/editor/standalone/browser/toggleHighContrast/toggleHighContrast.js'; +import { + CloseAction, + type Disposable, + ErrorAction, + type MessageTransports, + MonacoLanguageClient, + MonacoServices, + State, +} from 'monaco-languageclient'; +import React from 'react'; +import getMessageServiceOverride from 'vscode/service-override/messages'; +import { StandaloneServices } from 'vscode/services'; +import { + BrowserMessageReader, + BrowserMessageWriter, +} from 'vscode-languageserver-protocol/browser.js'; + +import JayveeMonarchConfig from './jayvee.monarch'; + +const LANGUAGE_NAME = 'jayvee'; + +export interface MonacoEditorProps { + startJayveeWorker: () => Worker; + + /** + * The text that shall (initially) be displayed in the editor. + * + * Whenever this Prop changes, the internal Model is re-created. + * This is a pretty expensive operation. + * Thus, you should only change this Prop when absolutely necessary. + * Most importantly: Since the editor holds its own state, you should **not** sync `editorText` with your State. + * Or, in other words: Do not update this Prop whenever {@link onDidChangeEditorText} is called. + * In most applications, it is sufficient to set this Prop once (when the component gets created) and then to just leave it unchanged. + */ + editorText: string; + onDidChangeEditorText: (newText: string) => void; + + /** + * Settings that will be passed to the constructor of the Monaco editor. + * These settings can be used to control the appearance of the editor. + * + * For instance, you can set the color theme of the editor using the following configuration: + * + * ``` ts + * { + * theme: 'vs-dark' + * } + * ``` + * + * A good starting point for experimenting with the editor options is the Monaco Playground, see + * https://microsoft.github.io/monaco-editor/playground.html + * + * **You should memoize this Prop, because whenever it changes, the editor model gets re-created.** + */ + editorConfig?: monaco.editor.IStandaloneEditorConstructionOptions; +} + +export const MonacoEditor: React.FC<MonacoEditorProps> = (props) => { + return ( + <MonacoContext {...props}> + <MonacoWrapper {...props} /> + </MonacoContext> + ); +}; + +const MonacoWrapper: React.FC<MonacoEditorProps> = (props) => { + const [state, setState] = React.useState<{ + model: monaco.editor.ITextModel | undefined; + }>({ + model: undefined, + }); + + React.useEffect(() => { + // For some reason, creating the model too quickly leads to a runtime error saying `Cannot read properties of undefined (reading 'onDidChangeNotification')`. Thus, we create the model "on-the-fly" here. + const model = monaco.editor.createModel(props.editorText, LANGUAGE_NAME); + + setState((state) => ({ ...state, model: model })); + + return () => { + model.dispose(); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + // Whenever the `editorText` Prop changes, update the model. + React.useEffect(() => { + if (!state.model) { + return; + } + + state.model.setValue(props.editorText); + }, [props.editorText, state.model]); + + React.useEffect(() => { + if (!state.model) { + return; + } + + // When the model content (i.e. the text in the editor) changes, call the callback function defined in Props. + state.model.onDidChangeContent(() => { + if (!state.model) { + return; + } + props.onDidChangeEditorText(state.model.getValue()); + }); + }, [props, state.model]); + + React.useEffect(() => { + if (!state.model) { + return; + } + + const defaultEditorConfig: monaco.editor.IStandaloneEditorConstructionOptions = + { + model: state.model, + theme: 'vs-light', + + // Make sure that the editor is automatically resized when the container is resized. + automaticLayout: true, + }; + const editorConfig = { ...defaultEditorConfig, ...props.editorConfig }; + + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const editor = monaco.editor.create(containerRef.current!, editorConfig); + + return () => { + editor.dispose(); + }; + }, [props.editorConfig, state.model]); + + const containerRef = React.useRef<HTMLDivElement>(null); + + return <div ref={containerRef} style={{ height: '100%' }}></div>; +}; + +interface MonacoContextProps extends MonacoEditorProps { + children: React.ReactNode; +} +const MonacoContext: React.FC<MonacoContextProps> = (props) => { + React.useLayoutEffect(() => { + const destroy = setUpMonaco(props.startJayveeWorker); + + return () => { + destroy(); + }; + }, [props.startJayveeWorker]); + + return <React.Fragment>{props.children}</React.Fragment>; +}; + +function setUpMonaco(startJayveeWorker: () => Worker): () => void { + const disposables: Disposable[] = []; + + window.MonacoEnvironment = {}; + window.MonacoEnvironment.getWorker = (workerId): Worker => { + if (workerId !== 'workerMain.js') { + throw Error('Tried to load a worker with an unknown ID.'); + } + + const worker = new Worker( + new URL('monaco-editor/esm/vs/editor/editor.worker.js', import.meta.url), + { + type: 'module', + }, + ); + return worker; + }; + + monaco.languages.register({ + id: LANGUAGE_NAME, + }); + const tokensProvider = monaco.languages.setMonarchTokensProvider( + LANGUAGE_NAME, + JayveeMonarchConfig, + ); + disposables.push(tokensProvider); + + const installedServices = MonacoServices.install() as Disposable; + disposables.push(installedServices); + + StandaloneServices.initialize({ + ...getMessageServiceOverride(), + }); + + const worker = startJayveeWorker(); + const reader = new BrowserMessageReader(worker); + const writer = new BrowserMessageWriter(worker); + + const languageClient = new MonacoLanguageClient({ + name: 'Jayvee Language Client', + clientOptions: { + documentSelector: [{ language: LANGUAGE_NAME }], + errorHandler: { + error: () => ({ action: ErrorAction.Continue }), + closed: () => ({ action: CloseAction.DoNotRestart }), + }, + }, + connectionProvider: { + get: (): Promise<MessageTransports> => { + return Promise.resolve({ reader, writer }); + }, + }, + }); + + void languageClient.start(); + + return () => { + for (const disposable of disposables) { + disposable.dispose(); + } + + void pollToStopLanguageClient(languageClient, worker); + }; +} + +async function pollToStopLanguageClient( + languageClient: MonacoLanguageClient, + worker: Worker, + attemptsLeft = 5, +): Promise<void> { + if (attemptsLeft < 1) { + console.warn('Failed to stop the language client.'); + return; + } + + if (languageClient.state === State.Running) { + await languageClient.dispose(); + worker.terminate(); + return; + } + + const WAIT_MILLISECONDS = 1000; + window.setTimeout(() => { + void pollToStopLanguageClient(languageClient, worker, attemptsLeft - 1); + }, WAIT_MILLISECONDS); +} diff --git a/libs/monaco-editor/tsconfig.json b/libs/monaco-editor/tsconfig.json new file mode 100644 index 00000000..1d48585c --- /dev/null +++ b/libs/monaco-editor/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "jsx": "react-jsx", + "lib": ["dom", "dom.iterable", "esnext", "WebWorker"], + "allowJs": false, + "esModuleInterop": false, + "allowSyntheticDefaultImports": true, + "strict": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + ], + "extends": "../../tsconfig.base.json" +} diff --git a/libs/monaco-editor/tsconfig.json.license b/libs/monaco-editor/tsconfig.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/monaco-editor/tsconfig.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/monaco-editor/tsconfig.lib.json b/libs/monaco-editor/tsconfig.lib.json new file mode 100644 index 00000000..116fc93f --- /dev/null +++ b/libs/monaco-editor/tsconfig.lib.json @@ -0,0 +1,22 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "types": ["node"] + }, + "files": [ + "../../node_modules/@nx/react/typings/cssmodule.d.ts", + "../../node_modules/@nx/react/typings/image.d.ts" + ], + "exclude": [ + "src/**/*.spec.ts", + "src/**/*.test.ts", + "src/**/*.spec.tsx", + "src/**/*.test.tsx", + "src/**/*.spec.js", + "src/**/*.test.js", + "src/**/*.spec.jsx", + "src/**/*.test.jsx" + ], + "include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx"] +} diff --git a/libs/monaco-editor/tsconfig.lib.json.license b/libs/monaco-editor/tsconfig.lib.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/libs/monaco-editor/tsconfig.lib.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/nx.json b/nx.json new file mode 100644 index 00000000..6486dab4 --- /dev/null +++ b/nx.json @@ -0,0 +1,54 @@ +{ + "$schema": "./node_modules/nx/schemas/nx-schema.json", + "targetDefaults": { + "build": { + "cache": true, + "executor": "@nx/esbuild:esbuild", + "dependsOn": ["^build"], + "inputs": ["production", "^production"], + "outputs": ["{options.outputPath}"], + "defaultConfiguration": "production", + "options": { + "main": "{projectRoot}/src/index.ts", + "outputPath": "dist/{projectRoot}", + "outputFileName": "main.js", + "tsConfig": "{projectRoot}/tsconfig.lib.json", + "assets": [ + { + "glob": "{projectRoot}/README.md", + "input": ".", + "output": "." + } + ], + "generatePackageJson": true + }, + "configurations": { + "development": {}, + "production": {} + } + }, + "@nx/eslint:lint": { + "inputs": ["default", "{workspaceRoot}/.eslintrc.json"], + "cache": true + } + }, + "namedInputs": { + "default": ["{projectRoot}/**/*", "sharedGlobals"], + "production": [ + "default", + "!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)", + "!{projectRoot}/tsconfig.spec.json", + "!{projectRoot}/.eslintrc.json", + "!{projectRoot}/src/test-setup.[jt]s" + ], + "sharedGlobals": ["{workspaceRoot}/babel.config.json"] + }, + "defaultProject": "interpreter", + "generators": { + "@nx/react": { + "application": { + "babel": true + } + } + } +} diff --git a/nx.json.license b/nx.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/nx.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/package-lock.json b/package-lock.json index 261454f3..7f6b72a3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,1681 +1,51429 @@ { - "name": "ss24-zeltner-columnar-data", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "dependencies": { - "@types/sqlite3": "^3.1.11", - "node-sqlite": "^0.0.2-security", - "nodejs-polars": "^0.10.0", - "sqlite3": "^5.1.7", - "ts-node": "^10.9.2" - }, - "devDependencies": { - "@types/node": "^20.12.7", - "typescript": "^5.4.5" - } - }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@gar/promisify": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", - "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", - "optional": true - }, - "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==", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "node_modules/@npmcli/fs": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", - "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", - "optional": true, - "dependencies": { - "@gar/promisify": "^1.0.1", - "semver": "^7.3.5" - } - }, - "node_modules/@npmcli/move-file": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", - "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", - "deprecated": "This functionality has been moved to @npmcli/fs", - "optional": true, - "dependencies": { - "mkdirp": "^1.0.4", - "rimraf": "^3.0.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "optional": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", - "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==" - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==" - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==" - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==" - }, - "node_modules/@types/node": { - "version": "20.12.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz", - "integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==", - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/@types/sqlite3": { - "version": "3.1.11", - "resolved": "https://registry.npmjs.org/@types/sqlite3/-/sqlite3-3.1.11.tgz", - "integrity": "sha512-KYF+QgxAnnAh7DWPdNDroxkDI3/MspH1NMx6m/N/6fT1G6+jvsw4/ZePt8R8cr7ta58aboeTfYFBDxTJ5yv15w==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "optional": true - }, - "node_modules/acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", - "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "optional": true, - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/agentkeepalive": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", - "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==", - "optional": true, - "dependencies": { - "humanize-ms": "^1.2.1" - }, - "engines": { - "node": ">= 8.0.0" - } - }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "optional": true, - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "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==", - "optional": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/aproba": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", - "optional": true - }, - "node_modules/are-we-there-yet": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", - "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", - "optional": true, - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" - }, - "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==", - "optional": 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==", - "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/bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "dependencies": { - "file-uri-to-path": "1.0.0" - } - }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "optional": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/cacache": { - "version": "15.3.0", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", - "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", - "optional": true, - "dependencies": { - "@npmcli/fs": "^1.0.0", - "@npmcli/move-file": "^1.0.1", - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "glob": "^7.1.4", - "infer-owner": "^1.0.4", - "lru-cache": "^6.0.0", - "minipass": "^3.1.1", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.2", - "mkdirp": "^1.0.3", - "p-map": "^4.0.0", - "promise-inflight": "^1.0.1", - "rimraf": "^3.0.2", - "ssri": "^8.0.1", - "tar": "^6.0.2", - "unique-filename": "^1.1.1" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "engines": { - "node": ">=10" - } - }, - "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==", - "optional": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "optional": true, - "bin": { - "color-support": "bin.js" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "optional": true - }, - "node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", - "optional": true - }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "optional": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", - "optional": true - }, - "node_modules/detect-libc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", - "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "engines": { - "node": ">=0.3.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==", - "optional": true - }, - "node_modules/encoding": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", - "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "optional": true, - "dependencies": { - "iconv-lite": "^0.6.2" - } - }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/env-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", - "optional": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/err-code": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", - "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", - "optional": true - }, - "node_modules/expand-template": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", - "engines": { - "node": ">=6" - } - }, - "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==" - }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" - }, - "node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "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==", - "optional": true - }, - "node_modules/gauge": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", - "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", - "optional": true, - "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.3", - "console-control-strings": "^1.1.0", - "has-unicode": "^2.0.1", - "signal-exit": "^3.0.7", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.5" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/github-from-package": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==" - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "optional": 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/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==", - "optional": true - }, - "node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", - "optional": true - }, - "node_modules/http-cache-semantics": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", - "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", - "optional": true - }, - "node_modules/http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "optional": true, - "dependencies": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "optional": true, - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/humanize-ms": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", - "optional": true, - "dependencies": { - "ms": "^2.0.0" - } - }, - "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "optional": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "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/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "optional": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "optional": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/infer-owner": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", - "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", - "optional": true - }, - "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==", - "optional": 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==" - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" - }, - "node_modules/ip-address": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", - "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", - "optional": true, - "dependencies": { - "jsbn": "1.1.0", - "sprintf-js": "^1.1.3" - }, - "engines": { - "node": ">= 12" - } - }, - "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==", - "optional": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-lambda": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", - "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", - "optional": true - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "optional": true - }, - "node_modules/jsbn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", - "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", - "optional": true - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" - }, - "node_modules/make-fetch-happen": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", - "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", - "optional": true, - "dependencies": { - "agentkeepalive": "^4.1.3", - "cacache": "^15.2.0", - "http-cache-semantics": "^4.1.0", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", - "is-lambda": "^1.0.1", - "lru-cache": "^6.0.0", - "minipass": "^3.1.3", - "minipass-collect": "^1.0.2", - "minipass-fetch": "^1.3.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.2", - "promise-retry": "^2.0.1", - "socks-proxy-agent": "^6.0.0", - "ssri": "^8.0.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "optional": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-collect": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", - "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", - "optional": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minipass-fetch": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", - "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", - "optional": true, - "dependencies": { - "minipass": "^3.1.0", - "minipass-sized": "^1.0.3", - "minizlib": "^2.0.0" - }, - "engines": { - "node": ">=8" - }, - "optionalDependencies": { - "encoding": "^0.1.12" - } - }, - "node_modules/minipass-flush": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", - "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", - "optional": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minipass-pipeline": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", - "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", - "optional": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-sized": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", - "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", - "optional": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "optional": true - }, - "node_modules/napi-build-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", - "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==" - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "optional": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/node-abi": { - "version": "3.57.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.57.0.tgz", - "integrity": "sha512-Dp+A9JWxRaKuHP35H77I4kCKesDy5HUDEmScia2FyncMTOXASMyg251F5PhFoDA5uqBrDDffiLpbqnrZmNXW+g==", - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-addon-api": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.0.tgz", - "integrity": "sha512-mNcltoe1R8o7STTegSOHdnJNN7s5EUvhoS7ShnTHDyOSd+8H+UdWODq6qSv67PjC8Zc5JRT8+oLAMCr0SIXw7g==", - "engines": { - "node": "^16 || ^18 || >= 20" - } - }, - "node_modules/node-gyp": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", - "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", - "optional": true, - "dependencies": { - "env-paths": "^2.2.0", - "glob": "^7.1.4", - "graceful-fs": "^4.2.6", - "make-fetch-happen": "^9.1.0", - "nopt": "^5.0.0", - "npmlog": "^6.0.0", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.2", - "which": "^2.0.2" - }, - "bin": { - "node-gyp": "bin/node-gyp.js" - }, - "engines": { - "node": ">= 10.12.0" - } - }, - "node_modules/node-sqlite": { - "version": "0.0.2-security", - "resolved": "https://registry.npmjs.org/node-sqlite/-/node-sqlite-0.0.2-security.tgz", - "integrity": "sha512-pX8yV5LM10MbmvF0vPHB7Vw79UCHemYaI8sEDO3MOpI49hRNyFZf+WD9s23fz9hghCX4IWzlJ1n5AgSPQPwBCw==" - }, - "node_modules/nodejs-polars": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/nodejs-polars/-/nodejs-polars-0.10.0.tgz", - "integrity": "sha512-IZ5QMnkVhk5f7AcTZy6F8V5sv9eIt6qx6T874KQhb711rQClfYx9iWxtRc1MxC8M1GGbSShAEv08QL4zi15H/g==", - "workspaces": [ - "benches" - ], - "engines": { - "node": ">= 18" - }, - "optionalDependencies": { - "nodejs-polars-android-arm64": "0.10.0", - "nodejs-polars-darwin-arm64": "0.10.0", - "nodejs-polars-darwin-x64": "0.10.0", - "nodejs-polars-linux-arm64-gnu": "0.10.0", - "nodejs-polars-linux-arm64-musl": "0.10.0", - "nodejs-polars-linux-x64-gnu": "0.10.0", - "nodejs-polars-linux-x64-musl": "0.10.0", - "nodejs-polars-win32-x64-msvc": "0.10.0" - } - }, - "node_modules/nodejs-polars-android-arm64": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/nodejs-polars-android-arm64/-/nodejs-polars-android-arm64-0.10.0.tgz", - "integrity": "sha512-Zx0pKahgbKozh3wtvW2rcUhjMPh8zJZ8p45gah+l5kSPmAhhOcdZmK1k6yGgoy+3F1NRy8Pia4mxMNnkeaHi0g==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 16" - } - }, - "node_modules/nodejs-polars-darwin-arm64": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/nodejs-polars-darwin-arm64/-/nodejs-polars-darwin-arm64-0.10.0.tgz", - "integrity": "sha512-TAtEFSmSoefSRz5mtUYPkbYp2W/HREvpEc4weS0/idameBOXmG077PlEGea1+YokePQQHpcPDHQ3AIYf/NcMnQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 16" - } - }, - "node_modules/nodejs-polars-darwin-x64": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/nodejs-polars-darwin-x64/-/nodejs-polars-darwin-x64-0.10.0.tgz", - "integrity": "sha512-D4FLtb6a5I9OsHbAB7DFFmDy/t2oegpP9Up+PnEZ+5QTWt8eOuB4BxLkzpXPN+fMkjKZaxOrq93QEw8Y/4YUdA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 16" - } - }, - "node_modules/nodejs-polars-linux-arm64-gnu": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/nodejs-polars-linux-arm64-gnu/-/nodejs-polars-linux-arm64-gnu-0.10.0.tgz", - "integrity": "sha512-YcA5FsG/RejtOAvu3+To27clcTgs19hOWvQiOm2waY/uLC6PpuUQ17JP3msCrXo1WjLWok4DY7JIOvV1y/+BzQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 16" - } - }, - "node_modules/nodejs-polars-linux-arm64-musl": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/nodejs-polars-linux-arm64-musl/-/nodejs-polars-linux-arm64-musl-0.10.0.tgz", - "integrity": "sha512-OVduD/2BhSHPvbMeWYLkZjBX61p4nYnO41OGcuNON6n5gN489LvawSQSB2znc6XT9LHmYl1uum8/Dalvz81wQg==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 16" - } - }, - "node_modules/nodejs-polars-linux-x64-gnu": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/nodejs-polars-linux-x64-gnu/-/nodejs-polars-linux-x64-gnu-0.10.0.tgz", - "integrity": "sha512-TeMUg1XCmJhkkFZblcVqHMPE+Qdd0JoAjObv05m+ztGaDzJ1gP7bFoUerXyahGFZlfCxRNpzrd57g5IEZcCJOg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 16" - } - }, - "node_modules/nodejs-polars-linux-x64-musl": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/nodejs-polars-linux-x64-musl/-/nodejs-polars-linux-x64-musl-0.10.0.tgz", - "integrity": "sha512-DIY+QB0TlizPBgmgpOsPAQcP08p+JDedke9L1OMZ3QSdaOBSHfEhuTfz5syVn0e7LDbZolC+gYbIpmyOB1ogbA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 16" - } - }, - "node_modules/nodejs-polars-win32-x64-msvc": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/nodejs-polars-win32-x64-msvc/-/nodejs-polars-win32-x64-msvc-0.10.0.tgz", - "integrity": "sha512-p+hsgET2XLAx3mf30OVJLk8wgI6NWoJqP4EdmEV4JUkDHApv88xz4iXLzKTU3YGSpj4sG9kSYfucvpeNvqyzqQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 16" - } - }, - "node_modules/nopt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", - "optional": true, - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/npmlog": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", - "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", - "optional": true, - "dependencies": { - "are-we-there-yet": "^3.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^4.0.3", - "set-blocking": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "optional": true, - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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==", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/prebuild-install": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.2.tgz", - "integrity": "sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==", - "dependencies": { - "detect-libc": "^2.0.0", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^1.0.1", - "node-abi": "^3.3.0", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^4.0.0", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" - }, - "bin": { - "prebuild-install": "bin.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/promise-inflight": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", - "optional": true - }, - "node_modules/promise-retry": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", - "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", - "optional": true, - "dependencies": { - "err-code": "^2.0.2", - "retry": "^0.12.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", - "optional": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "optional": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "optional": true - }, - "node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "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==", - "optional": true - }, - "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==", - "optional": true - }, - "node_modules/simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "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/simple-get": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", - "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", - "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": { - "decompress-response": "^6.0.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, - "node_modules/smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "optional": true, - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", - "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", - "optional": true, - "dependencies": { - "ip-address": "^9.0.5", - "smart-buffer": "^4.2.0" - }, - "engines": { - "node": ">= 10.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks-proxy-agent": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", - "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", - "optional": true, - "dependencies": { - "agent-base": "^6.0.2", - "debug": "^4.3.3", - "socks": "^2.6.2" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/sprintf-js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", - "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", - "optional": true - }, - "node_modules/sqlite3": { - "version": "5.1.7", - "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.1.7.tgz", - "integrity": "sha512-GGIyOiFaG+TUra3JIfkI/zGP8yZYLPQ0pl1bH+ODjiX57sPhrLU5sQJn1y9bDKZUFYkX1crlrPfSYt0BKKdkog==", - "hasInstallScript": true, - "dependencies": { - "bindings": "^1.5.0", - "node-addon-api": "^7.0.0", - "prebuild-install": "^7.1.1", - "tar": "^6.1.11" - }, - "optionalDependencies": { - "node-gyp": "8.x" - }, - "peerDependencies": { - "node-gyp": "8.x" - }, - "peerDependenciesMeta": { - "node-gyp": { - "optional": true - } - } - }, - "node_modules/ssri": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", - "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", - "optional": true, - "dependencies": { - "minipass": "^3.1.1" - }, - "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==", - "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==", - "optional": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "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==", - "optional": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/tar": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", - "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", - "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/tar-fs": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", - "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", - "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - } - }, - "node_modules/tar-fs/node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" - }, - "node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tar/node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/ts-node": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", - "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/typescript": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", - "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" - }, - "node_modules/unique-filename": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", - "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", - "optional": true, - "dependencies": { - "unique-slug": "^2.0.0" - } - }, - "node_modules/unique-slug": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", - "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", - "optional": true, - "dependencies": { - "imurmurhash": "^0.1.4" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==" - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "optional": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "optional": true, - "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "engines": { - "node": ">=6" - } - } - } + "name": "jayvee", + "version": "0.4.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "jayvee", + "version": "0.4.0", + "dependencies": { + "@docusaurus/core": "2.4.1", + "@docusaurus/preset-classic": "2.4.1", + "@docusaurus/theme-mermaid": "2.4.1", + "@mdx-js/react": "^1.6.22", + "assert": "^2.0.0", + "chalk": "^4.1.2", + "clsx": "^1.2.1", + "commander": "^8.0.0", + "exceljs": "^4.3.0", + "fast-csv": "^4.3.6", + "follow-redirects": "^1.15.2", + "fp-ts": "^2.16.5", + "gtfs-realtime-bindings": "^1.1.1", + "jszip": "^3.10.1", + "langium": "^3.0.0", + "mime-types": "^2.1.35", + "monaco-editor": "^0.34.1", + "monaco-languageclient": "^4.0.3", + "nodejs-polars": "^0.11.0", + "pg": "^8.8.0", + "prism-react-renderer": "^1.3.5", + "react": "18.2.0", + "react-dom": "18.2.0", + "sqlite3": "^5.1.5", + "tslib": "^2.3.0", + "vscode-languageclient": "^9.0.1", + "vscode-languageserver": "^9.0.1", + "vscode-languageserver-protocol": "^3.17.2", + "vscode-uri": "^3.0.8" + }, + "devDependencies": { + "@babel/core": "^7.14.5", + "@babel/preset-react": "^7.14.5", + "@docusaurus/module-type-aliases": "2.4.1", + "@jvalue/eslint-config-jvalue": "^1.3.0", + "@nx-plus/docusaurus": "^15.0.0-rc.0", + "@nx/esbuild": "18.0.1", + "@nx/eslint": "18.0.1", + "@nx/eslint-plugin": "18.0.1", + "@nx/js": "18.3.4", + "@nx/node": "18.0.1", + "@nx/react": "18.0.1", + "@nx/rollup": "18.0.1", + "@nx/vite": "^18.3.4", + "@nx/web": "18.3.4", + "@nx/webpack": "18.0.1", + "@nx/workspace": "18.0.1", + "@swc-node/register": "~1.8.0", + "@swc/core": "~1.3.85", + "@swc/helpers": "~0.5.2", + "@types/follow-redirects": "^1.14.1", + "@types/mime-types": "^2.1.1", + "@types/node": "18.19.11", + "@types/pg": "^8.6.5", + "@types/react": "18.2.24", + "@types/react-dom": "18.2.9", + "@types/vscode": "^1.56.0", + "@typescript-eslint/eslint-plugin": "6.20.0", + "@typescript-eslint/parser": "6.20.0", + "@vitest/coverage-v8": "^1.0.4", + "@vitest/ui": "^1.3.1", + "@vscode/vsce": "^2.19.0", + "esbuild": "^0.19.2", + "eslint": "^8.48.0", + "eslint-config-prettier": "9.1.0", + "eslint-config-react-app": "^7.0.1", + "eslint-plugin-import": "2.27.5", + "eslint-plugin-prettier": "^4.2.1", + "eslint-plugin-react": "7.32.2", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-unicorn": "^52.0.0", + "eslint-plugin-vitest": "^0.5.4", + "jest-environment-jsdom": "^29.7.0", + "langium-cli": "^3.0.0", + "nock": "13.3.1", + "nx": "18.0.1", + "prettier": "^2.8.7", + "ts-node": "10.9.1", + "typescript": "^5.0.4", + "vite": "~5.0.0", + "vitest": "^1.3.1" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@adobe/css-tools": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.3.tgz", + "integrity": "sha512-rE0Pygv0sEZ4vBWHlAgJLGDU7Pm8xoO6p3wsEceb7GYAjScrOHpEo8KK/eVkAcnSM+slAEtXjA2JpdjLp4fJQQ==", + "dev": true + }, + "node_modules/@algolia/autocomplete-core": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.9.2.tgz", + "integrity": "sha512-hkG80c9kx9ClVAEcUJbTd2ziVC713x9Bji9Ty4XJfKXlxlsx3iXsoNhAwfeR4ulzIUg7OE5gez0UU1zVDdG7kg==", + "dependencies": { + "@algolia/autocomplete-plugin-algolia-insights": "1.9.2", + "@algolia/autocomplete-shared": "1.9.2" + } + }, + "node_modules/@algolia/autocomplete-plugin-algolia-insights": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.9.2.tgz", + "integrity": "sha512-2LVsf4W66hVHQ3Ua/8k15oPlxjELCztbAkQm/hP42Sw+GLkHAdY1vaVRYziaWq64+Oljfg6FKkZHCdgXH+CGIA==", + "dependencies": { + "@algolia/autocomplete-shared": "1.9.2" + }, + "peerDependencies": { + "search-insights": ">= 1 < 3" + } + }, + "node_modules/@algolia/autocomplete-preset-algolia": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.9.2.tgz", + "integrity": "sha512-pqgIm2GNqtCT59Y1ICctIPrYTi34+wNPiNWEclD/yDzp5uDUUsyGe5XrUjCNyQRTKonAlmYxoaEHOn8FWgmBHA==", + "dependencies": { + "@algolia/autocomplete-shared": "1.9.2" + }, + "peerDependencies": { + "@algolia/client-search": ">= 4.9.1 < 6", + "algoliasearch": ">= 4.9.1 < 6" + } + }, + "node_modules/@algolia/autocomplete-shared": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.9.2.tgz", + "integrity": "sha512-XxX6YDn+7LG+SmdpXEOnj7fc3TjiVpQ0CbGhjLwrd2tYr6LVY2D4Iiu/iuYJ4shvVDWWnpwArSk0uIWC/8OPUA==", + "peerDependencies": { + "@algolia/client-search": ">= 4.9.1 < 6", + "algoliasearch": ">= 4.9.1 < 6" + } + }, + "node_modules/@algolia/cache-browser-local-storage": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.17.2.tgz", + "integrity": "sha512-ZkVN7K/JE+qMQbpR6h3gQOGR6yCJpmucSBCmH5YDxnrYbp2CbrVCu0Nr+FGVoWzMJNznj1waShkfQ9awERulLw==", + "dependencies": { + "@algolia/cache-common": "4.17.2" + } + }, + "node_modules/@algolia/cache-common": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.17.2.tgz", + "integrity": "sha512-fojbhYIS8ovfYs6hwZpy1O4mBfVRxNgAaZRqsdVQd54hU4MxYDYFCxagYX28lOBz7btcDHld6BMoWXvjzkx6iQ==" + }, + "node_modules/@algolia/cache-in-memory": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.17.2.tgz", + "integrity": "sha512-UYQcMzPurNi+cPYkuPemTZkjKAjdgAS1hagC5irujKbrYnN4yscK4TkOI5tX+O8/KegtJt3kOK07OIrJ2QDAAw==", + "dependencies": { + "@algolia/cache-common": "4.17.2" + } + }, + "node_modules/@algolia/client-account": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.17.2.tgz", + "integrity": "sha512-doSk89pBPDpDyKJSHFADIGa2XSGrBCj3QwPvqtRJXDADpN+OjW+eTR8r4hEs/7X4GGfjfAOAES8JgDx+fZntYw==", + "dependencies": { + "@algolia/client-common": "4.17.2", + "@algolia/client-search": "4.17.2", + "@algolia/transporter": "4.17.2" + } + }, + "node_modules/@algolia/client-analytics": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.17.2.tgz", + "integrity": "sha512-V+DcXbOtD/hKwAR3qGQrtlrJ3q2f9OKfx843q744o4m3xHv5ueCAvGXB1znPsdaUrVDNAImcgEgqwI9x7EJbDw==", + "dependencies": { + "@algolia/client-common": "4.17.2", + "@algolia/client-search": "4.17.2", + "@algolia/requester-common": "4.17.2", + "@algolia/transporter": "4.17.2" + } + }, + "node_modules/@algolia/client-common": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.17.2.tgz", + "integrity": "sha512-gKBUnjxi0ukJYIJxVREYGt1Dmj1B3RBYbfGWi0dIPp1BC1VvQm+BOuNwsIwmq/x3MPO+sGuK978eKiP3tZDvag==", + "dependencies": { + "@algolia/requester-common": "4.17.2", + "@algolia/transporter": "4.17.2" + } + }, + "node_modules/@algolia/client-personalization": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.17.2.tgz", + "integrity": "sha512-wc4UgOWxSYWz5wpuelNmlt895jA9twjZWM2ms17Ws8qCvBHF7OVGdMGgbysPB8790YnfvvDnSsWOv3CEj26Eow==", + "dependencies": { + "@algolia/client-common": "4.17.2", + "@algolia/requester-common": "4.17.2", + "@algolia/transporter": "4.17.2" + } + }, + "node_modules/@algolia/client-search": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.17.2.tgz", + "integrity": "sha512-FUjIs+gRe0upJC++uVs4sdxMw15JxfkT86Gr/kqVwi9kcqaZhXntSbW/Fw959bIYXczjmeVQsilYvBWW4YvSZA==", + "dependencies": { + "@algolia/client-common": "4.17.2", + "@algolia/requester-common": "4.17.2", + "@algolia/transporter": "4.17.2" + } + }, + "node_modules/@algolia/events": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@algolia/events/-/events-4.0.1.tgz", + "integrity": "sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ==" + }, + "node_modules/@algolia/logger-common": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.17.2.tgz", + "integrity": "sha512-EfXuweUE+1HiSMsQidaDWA5Lv4NnStYIlh7PO5pLkI+sdhbMX0e5AO5nUAMIFM1VkEANes70RA8fzhP6OqCqQQ==" + }, + "node_modules/@algolia/logger-console": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.17.2.tgz", + "integrity": "sha512-JuG8HGVlJ+l/UEDK4h2Y8q/IEmRjQz1J0aS9tf6GPNbGYiSvMr1DDdZ+hqV3bb1XE6wU8Ypex56HisWMSpnG0A==", + "dependencies": { + "@algolia/logger-common": "4.17.2" + } + }, + "node_modules/@algolia/requester-browser-xhr": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.17.2.tgz", + "integrity": "sha512-FKI2lYWwksALfRt2OETFmGb5+P7WVc4py2Ai3H7k8FSfTLwVvs9WVVmtlx6oANQ8RFEK4B85h8DQJTJ29TDfmA==", + "dependencies": { + "@algolia/requester-common": "4.17.2" + } + }, + "node_modules/@algolia/requester-common": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.17.2.tgz", + "integrity": "sha512-Rfim23ztAhYpE9qm+KCfCRo+YLJCjiiTG+IpDdzUjMpYPhUtirQT0A35YEd/gKn86YNyydxS9w8iRSjwKh+L0A==" + }, + "node_modules/@algolia/requester-node-http": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.17.2.tgz", + "integrity": "sha512-E0b0kyCDMvUIhQmDNd/mH4fsKJdEEX6PkMKrYJjzm6moo+rP22tqpq4Rfe7DZD8OB6/LsDD3zs3Kvd+L+M5wwQ==", + "dependencies": { + "@algolia/requester-common": "4.17.2" + } + }, + "node_modules/@algolia/transporter": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.17.2.tgz", + "integrity": "sha512-m8pXlz5OnNzjD1rcw+duCN4jG4yEzkJBsvKYMoN22Oq6rQwy1AY5muZ+IQUs4dL+A364CYkRMLRWhvXpCZ1x+g==", + "dependencies": { + "@algolia/cache-common": "4.17.2", + "@algolia/logger-common": "4.17.2", + "@algolia/requester-common": "4.17.2" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", + "dependencies": { + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/code-frame/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/code-frame/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/@babel/code-frame/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", + "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.9.tgz", + "integrity": "sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw==", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.23.9", + "@babel/parser": "^7.23.9", + "@babel/template": "^7.23.9", + "@babel/traverse": "^7.23.9", + "@babel/types": "^7.23.9", + "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/core/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==" + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/eslint-parser": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.19.1.tgz", + "integrity": "sha512-AqNf2QWt1rtu2/1rLswy6CDP7H9Oh3mMhk177Y67Rg8d7RD9WfOLLv8CGn6tisFvS2htm86yIe1yLF6I1UDaGQ==", + "dev": true, + "dependencies": { + "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", + "eslint-visitor-keys": "^2.1.0", + "semver": "^6.3.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || >=14.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.11.0", + "eslint": "^7.5.0 || ^8.0.0" + } + }, + "node_modules/@babel/eslint-parser/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/@babel/eslint-parser/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", + "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", + "dependencies": { + "@babel/types": "^7.23.6", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", + "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz", + "integrity": "sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==", + "dependencies": { + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", + "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", + "dependencies": { + "@babel/compat-data": "^7.23.5", + "@babel/helper-validator-option": "^7.23.5", + "browserslist": "^4.22.2", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/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==", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.23.9.tgz", + "integrity": "sha512-B2L9neXTIyPQoXDm+NtovPvG6VOLWnaXu3BIeVDWwdKFgG30oNa6CqVGiJPDWQwIAK49t9gnQI9c6K6RzabiKw==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-member-expression-to-functions": "^7.23.0", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.20", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz", + "integrity": "sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "regexpu-core": "^5.3.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.5.0.tgz", + "integrity": "sha512-NovQquuQLAQ5HuyjCz7WQP9MjRj7dx++yspwiyUiGl9ZyadHRSql1HZh5ogRd8W8w6YM6EQ/NTB8rgjLt5W65Q==", + "dependencies": { + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz", + "integrity": "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==", + "dependencies": { + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", + "dependencies": { + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", + "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", + "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz", + "integrity": "sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-wrap-function": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz", + "integrity": "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-member-expression-to-functions": "^7.22.15", + "@babel/helper-optimise-call-expression": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", + "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz", + "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz", + "integrity": "sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.20.tgz", + "integrity": "sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw==", + "dependencies": { + "@babel/helper-function-name": "^7.22.5", + "@babel/template": "^7.22.15", + "@babel/types": "^7.22.19" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.9.tgz", + "integrity": "sha512-87ICKgU5t5SzOT7sBMfCOZQ2rHjRU+Pcb9BoILMYz600W6DkVRLFBPwQ18gwUVvggqXivaUakpnxWQGbpywbBQ==", + "dependencies": { + "@babel/template": "^7.23.9", + "@babel/traverse": "^7.23.9", + "@babel/types": "^7.23.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.5.tgz", + "integrity": "sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.23.3.tgz", + "integrity": "sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.23.3.tgz", + "integrity": "sha512-WwlxbfMNdVEpQjZmK5mhm7oSwD3dS6eU+Iwsi4Knl9wAletWem7kaRsGOG+8UEbRyqxY4SS5zvtfXwX+jMxUwQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/plugin-transform-optional-chaining": "^7.23.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.23.7.tgz", + "integrity": "sha512-LlRT7HgaifEpQA1ZgLVOIJZZFVPWN5iReq/7/JixwBtwcoeVGDBD53ZV28rrsLYOZs1Y/EHhA8N/Z6aazHR8cw==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-proposal-class-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", + "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-decorators": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.23.9.tgz", + "integrity": "sha512-hJhBCb0+NnTWybvWq2WpbCYDOcflSbx0t+BYP65e5R9GVnukiDTi+on5bFkk4p7QGuv190H6KfNiV9Knf/3cZA==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.23.9", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-decorators": "^7.23.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", + "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-numeric-separator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", + "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-chaining": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.9.tgz", + "integrity": "sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-methods": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", + "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-decorators": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.23.3.tgz", + "integrity": "sha512-cf7Niq4/+/juY67E0PbgH0TDhLQ5J7zS8C/Q5FFx+DWyrRa9sUQdTXkjqKu8zGvuqr7vw1muKiukseihU+PJDA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-flow": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.18.6.tgz", + "integrity": "sha512-LUbR+KNTBWCUAqRG9ex5Gnzu2IOkt8jRJbHHXFT9q+L9zm7M/QQbEqXyw1n1pohYvOyWC8CjeyjrSaIwiYjK7A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.23.3.tgz", + "integrity": "sha512-lPgDSU+SJLK3xmFDTV2ZRQAiM7UuUjGidwBywFavObCiZc1BeAAcMtHJKUya92hPHO+at63JJPLygilZard8jw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.23.3.tgz", + "integrity": "sha512-pawnE0P9g10xgoP7yKr6CK63K2FMsTE+FZidZO/1PwRdzmAPVs+HS1mAURUsgaoxammTJvULUdIkEK0gOcU2tA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz", + "integrity": "sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz", + "integrity": "sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.23.3.tgz", + "integrity": "sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-generator-functions": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.9.tgz", + "integrity": "sha512-8Q3veQEDGe14dTYuwagbRtwxQDnytyg1JFu4/HwEMETeofocrB0U0ejBJIXoeG/t2oXZ8kzCyI0ZZfbT80VFNQ==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-remap-async-to-generator": "^7.22.20", + "@babel/plugin-syntax-async-generators": "^7.8.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.23.3.tgz", + "integrity": "sha512-A7LFsKi4U4fomjqXJlZg/u0ft/n8/7n7lpffUP/ZULx/DtV9SGlNKZolHH6PE8Xl1ngCc0M11OaeZptXVkfKSw==", + "dependencies": { + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-remap-async-to-generator": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.23.3.tgz", + "integrity": "sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.4.tgz", + "integrity": "sha512-0QqbP6B6HOh7/8iNR4CQU2Th/bbRtBp4KS9vcaZd1fZ0wSh5Fyssg0UCIHwxh+ka+pNDREbVLQnHCMHKZfPwfw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.23.3.tgz", + "integrity": "sha512-uM+AN8yCIjDPccsKGlw271xjJtGii+xQIF/uMPS8H15L12jZTsLfF4o5vNO7d/oUguOyfdikHGc/yi9ge4SGIg==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.23.4.tgz", + "integrity": "sha512-nsWu/1M+ggti1SOALj3hfx5FXzAY06fwPJsUZD4/A5e1bWi46VUIWtD+kOX6/IdhXGsXBWllLFDSnqSCdUNydQ==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.23.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.23.8.tgz", + "integrity": "sha512-yAYslGsY1bX6Knmg46RjiCiNSwJKv2IUC8qOdYKqMMr0491SXFhcHqOdRDeCRohOOIzwN/90C6mQ9qAKgrP7dg==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.20", + "@babel/helper-split-export-declaration": "^7.22.6", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.23.3.tgz", + "integrity": "sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/template": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.23.3.tgz", + "integrity": "sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.23.3.tgz", + "integrity": "sha512-vgnFYDHAKzFaTVp+mneDsIEbnJ2Np/9ng9iviHw3P/KVcgONxpNULEW/51Z/BaFojG2GI2GwwXck5uV1+1NOYQ==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.23.3.tgz", + "integrity": "sha512-RrqQ+BQmU3Oyav3J+7/myfvRCq7Tbz+kKLLshUmMwNlDHExbGL7ARhajvoBJEvc+fCguPPu887N+3RRXBVKZUA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dynamic-import": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.23.4.tgz", + "integrity": "sha512-V6jIbLhdJK86MaLh4Jpghi8ho5fGzt3imHOBu/x0jlBaPYqDoWz4RDXjmMOfnh+JWNaQleEAByZLV0QzBT4YQQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.23.3.tgz", + "integrity": "sha512-5fhCsl1odX96u7ILKHBj4/Y8vipoqwsJMh4csSA8qFfxrZDEA4Ssku2DyNvMJSmZNOEBT750LfFPbtrnTP90BQ==", + "dependencies": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-export-namespace-from": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.23.4.tgz", + "integrity": "sha512-GzuSBcKkx62dGzZI1WVgTWvkkz84FZO5TC5T8dl/Tht/rAla6Dg/Mz9Yhypg+ezVACf/rgDuQt3kbWEv7LdUDQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-flow-strip-types": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.19.0.tgz", + "integrity": "sha512-sgeMlNaQVbCSpgLSKP4ZZKfsJVnFnNQlUSk6gPYzR/q7tzCgQF2t8RBKAP6cKJeZdveei7Q7Jm527xepI8lNLg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/plugin-syntax-flow": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.23.6.tgz", + "integrity": "sha512-aYH4ytZ0qSuBbpfhuofbg/e96oQ7U2w1Aw/UQmKT+1l39uEhUPoFS3fHevDc1G0OvewyDudfMKY1OulczHzWIw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.23.3.tgz", + "integrity": "sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw==", + "dependencies": { + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-json-strings": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.23.4.tgz", + "integrity": "sha512-81nTOqM1dMwZ/aRXQ59zVubN9wHGqk6UtqRK+/q+ciXmRy8fSolhGVvG09HHRGo4l6fr/c4ZhXUQH0uFW7PZbg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-json-strings": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.23.3.tgz", + "integrity": "sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-logical-assignment-operators": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.23.4.tgz", + "integrity": "sha512-Mc/ALf1rmZTP4JKKEhUwiORU+vcfarFVLfcFiolKUo6sewoxSEgl36ak5t+4WamRsNr6nzjZXQjM35WsU+9vbg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.23.3.tgz", + "integrity": "sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.23.3.tgz", + "integrity": "sha512-vJYQGxeKM4t8hYCKVBlZX/gtIY2I7mRGFNcm85sgXGMTBcoV3QdVtdpbcWEbzbfUIUZKwvgFT82mRvaQIebZzw==", + "dependencies": { + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.3.tgz", + "integrity": "sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==", + "dependencies": { + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-simple-access": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.9.tgz", + "integrity": "sha512-KDlPRM6sLo4o1FkiSlXoAa8edLXFsKKIda779fbLrvmeuc3itnjCtaO6RrtoaANsIJANj+Vk1zqbZIMhkCAHVw==", + "dependencies": { + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.23.3.tgz", + "integrity": "sha512-zHsy9iXX2nIsCBFPud3jKn1IRPWg3Ing1qOZgeKV39m1ZgIdpJqvlWVeiHBZC6ITRG0MfskhYe9cLgntfSFPIg==", + "dependencies": { + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz", + "integrity": "sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.23.3.tgz", + "integrity": "sha512-YJ3xKqtJMAT5/TIZnpAR3I+K+WaDowYbN3xyxI8zxx/Gsypwf9B9h0VB+1Nh6ACAAPRS5NSRje0uVv5i79HYGQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.23.4.tgz", + "integrity": "sha512-jHE9EVVqHKAQx+VePv5LLGHjmHSJR76vawFPTdlxR/LVJPfOEGxREQwQfjuZEOPTwG92X3LINSh3M40Rv4zpVA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-numeric-separator": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.23.4.tgz", + "integrity": "sha512-mps6auzgwjRrwKEZA05cOwuDc9FAzoyFS4ZsG/8F43bTLf/TgkJg7QXOrPO1JO599iA3qgK9MXdMGOEC8O1h6Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-rest-spread": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.23.4.tgz", + "integrity": "sha512-9x9K1YyeQVw0iOXJlIzwm8ltobIIv7j2iLyP2jIhEbqPRQ7ScNgwQufU2I0Gq11VjyG4gI4yMXt2VFags+1N3g==", + "dependencies": { + "@babel/compat-data": "^7.23.3", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.23.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.23.3.tgz", + "integrity": "sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-catch-binding": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.23.4.tgz", + "integrity": "sha512-XIq8t0rJPHf6Wvmbn9nFxU6ao4c7WhghTR5WyV8SrJfUFzyxhCm4nhC+iAp3HFhbAKLfYpgzhJ6t4XCtVwqO5A==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.4.tgz", + "integrity": "sha512-ZU8y5zWOfjM5vZ+asjgAPwDaBjJzgufjES89Rs4Lpq63O300R/kOz30WCLo6BxxX6QVEilwSlpClnG5cZaikTA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.23.3.tgz", + "integrity": "sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.23.3.tgz", + "integrity": "sha512-UzqRcRtWsDMTLrRWFvUBDwmw06tCQH9Rl1uAjfh6ijMSmGYQ+fpdB+cnqRC8EMh5tuuxSv0/TejGL+7vyj+50g==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-property-in-object": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.23.4.tgz", + "integrity": "sha512-9G3K1YqTq3F4Vt88Djx1UZ79PDyj+yKRnUy7cZGSMe+a7jkwD259uKKuUzQlPkGam7R+8RJwh5z4xO27fA1o2A==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.23.3.tgz", + "integrity": "sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-constant-elements": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.23.3.tgz", + "integrity": "sha512-zP0QKq/p6O42OL94udMgSfKXyse4RyJ0JqbQ34zDAONWjyrEsghYEyTSK5FIpmXmCpB55SHokL1cRRKHv8L2Qw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-display-name": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.18.6.tgz", + "integrity": "sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.19.0.tgz", + "integrity": "sha512-UVEvX3tXie3Szm3emi1+G63jyw1w5IcMY0FSKM+CRnKRI5Mr1YbCNgsSTwoTwKphQEG9P+QqmuRFneJPZuHNhg==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/plugin-syntax-jsx": "^7.18.6", + "@babel/types": "^7.19.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-development": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.18.6.tgz", + "integrity": "sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA==", + "dependencies": { + "@babel/plugin-transform-react-jsx": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-pure-annotations": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.18.6.tgz", + "integrity": "sha512-I8VfEPg9r2TRDdvnHgPepTKvuRomzA8+u+nhY7qSI1fR2hRNebasZEETLyM5mAUr0Ku56OkXJ0I7NHJnO6cJiQ==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.23.3.tgz", + "integrity": "sha512-KP+75h0KghBMcVpuKisx3XTu9Ncut8Q8TuvGO4IhY+9D5DFEckQefOuIsB/gQ2tG71lCke4NMrtIPS8pOj18BQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "regenerator-transform": "^0.15.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.23.3.tgz", + "integrity": "sha512-QnNTazY54YqgGxwIexMZva9gqbPa15t/x9VS+0fsEFWplwVpXYZivtgl43Z1vMpc1bdPP2PP8siFeVcnFvA3Cg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.23.9.tgz", + "integrity": "sha512-A7clW3a0aSjm3ONU9o2HAILSegJCYlEZmOhmBRReVtIpY/Z/p7yIZ+wR41Z+UipwdGuqwtID/V/dOdZXjwi9gQ==", + "dependencies": { + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "babel-plugin-polyfill-corejs2": "^0.4.8", + "babel-plugin-polyfill-corejs3": "^0.9.0", + "babel-plugin-polyfill-regenerator": "^0.5.5", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.23.3.tgz", + "integrity": "sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.23.3.tgz", + "integrity": "sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.23.3.tgz", + "integrity": "sha512-HZOyN9g+rtvnOU3Yh7kSxXrKbzgrm5X4GncPY1QOquu7epga5MxKHVpYu2hvQnry/H+JjckSYRb93iNfsioAGg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.23.3.tgz", + "integrity": "sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.23.3.tgz", + "integrity": "sha512-4t15ViVnaFdrPC74be1gXBSMzXk3B4Us9lP7uLRQHTFpV5Dvt33pn+2MyyNxmN3VTTm3oTrZVMUmuw3oBnQ2oQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.23.6.tgz", + "integrity": "sha512-6cBG5mBvUu4VUD04OHKnYzbuHNP8huDsD3EDqqpIpsswTDoqHCjLoHb6+QgsV1WsT2nipRqCPgxD3LXnEO7XfA==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.23.6", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-typescript": "^7.23.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.23.3.tgz", + "integrity": "sha512-OMCUx/bU6ChE3r4+ZdylEqAjaQgHAgipgW8nsCfu5pGqDcFytVd91AwRvUJSBZDz0exPGgnjoqhgRYLRjFZc9Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-property-regex": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.23.3.tgz", + "integrity": "sha512-KcLIm+pDZkWZQAFJ9pdfmh89EwVfmNovFBcXko8szpBeF8z68kWIPeKlmSOkT9BXJxs2C0uk+5LxoxIv62MROA==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.23.3.tgz", + "integrity": "sha512-wMHpNA4x2cIA32b/ci3AfwNgheiva2W0WUKWTK7vBHBhDKfPsc5cFGNWm69WBqpwd86u1qwZ9PWevKqm1A3yAw==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-sets-regex": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.23.3.tgz", + "integrity": "sha512-W7lliA/v9bNR83Qc3q1ip9CQMZ09CcHDbHfbLRDNuAhn1Mvkr1ZNF7hPmztMQvtTGVLJ9m8IZqWsTkXOml8dbw==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.23.9.tgz", + "integrity": "sha512-3kBGTNBBk9DQiPoXYS0g0BYlwTQYUTifqgKTjxUwEUkduRT2QOa0FPGBJ+NROQhGyYO5BuTJwGvBnqKDykac6A==", + "dependencies": { + "@babel/compat-data": "^7.23.5", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-option": "^7.23.5", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.23.3", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.23.3", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.23.7", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-import-assertions": "^7.23.3", + "@babel/plugin-syntax-import-attributes": "^7.23.3", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.23.3", + "@babel/plugin-transform-async-generator-functions": "^7.23.9", + "@babel/plugin-transform-async-to-generator": "^7.23.3", + "@babel/plugin-transform-block-scoped-functions": "^7.23.3", + "@babel/plugin-transform-block-scoping": "^7.23.4", + "@babel/plugin-transform-class-properties": "^7.23.3", + "@babel/plugin-transform-class-static-block": "^7.23.4", + "@babel/plugin-transform-classes": "^7.23.8", + "@babel/plugin-transform-computed-properties": "^7.23.3", + "@babel/plugin-transform-destructuring": "^7.23.3", + "@babel/plugin-transform-dotall-regex": "^7.23.3", + "@babel/plugin-transform-duplicate-keys": "^7.23.3", + "@babel/plugin-transform-dynamic-import": "^7.23.4", + "@babel/plugin-transform-exponentiation-operator": "^7.23.3", + "@babel/plugin-transform-export-namespace-from": "^7.23.4", + "@babel/plugin-transform-for-of": "^7.23.6", + "@babel/plugin-transform-function-name": "^7.23.3", + "@babel/plugin-transform-json-strings": "^7.23.4", + "@babel/plugin-transform-literals": "^7.23.3", + "@babel/plugin-transform-logical-assignment-operators": "^7.23.4", + "@babel/plugin-transform-member-expression-literals": "^7.23.3", + "@babel/plugin-transform-modules-amd": "^7.23.3", + "@babel/plugin-transform-modules-commonjs": "^7.23.3", + "@babel/plugin-transform-modules-systemjs": "^7.23.9", + "@babel/plugin-transform-modules-umd": "^7.23.3", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", + "@babel/plugin-transform-new-target": "^7.23.3", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.23.4", + "@babel/plugin-transform-numeric-separator": "^7.23.4", + "@babel/plugin-transform-object-rest-spread": "^7.23.4", + "@babel/plugin-transform-object-super": "^7.23.3", + "@babel/plugin-transform-optional-catch-binding": "^7.23.4", + "@babel/plugin-transform-optional-chaining": "^7.23.4", + "@babel/plugin-transform-parameters": "^7.23.3", + "@babel/plugin-transform-private-methods": "^7.23.3", + "@babel/plugin-transform-private-property-in-object": "^7.23.4", + "@babel/plugin-transform-property-literals": "^7.23.3", + "@babel/plugin-transform-regenerator": "^7.23.3", + "@babel/plugin-transform-reserved-words": "^7.23.3", + "@babel/plugin-transform-shorthand-properties": "^7.23.3", + "@babel/plugin-transform-spread": "^7.23.3", + "@babel/plugin-transform-sticky-regex": "^7.23.3", + "@babel/plugin-transform-template-literals": "^7.23.3", + "@babel/plugin-transform-typeof-symbol": "^7.23.3", + "@babel/plugin-transform-unicode-escapes": "^7.23.3", + "@babel/plugin-transform-unicode-property-regex": "^7.23.3", + "@babel/plugin-transform-unicode-regex": "^7.23.3", + "@babel/plugin-transform-unicode-sets-regex": "^7.23.3", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "babel-plugin-polyfill-corejs2": "^0.4.8", + "babel-plugin-polyfill-corejs3": "^0.9.0", + "babel-plugin-polyfill-regenerator": "^0.5.5", + "core-js-compat": "^3.31.0", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/preset-react": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.18.6.tgz", + "integrity": "sha512-zXr6atUmyYdiWRVLOZahakYmOBHtWc2WGCkP8PYTgZi0iJXDY2CN180TdrIW4OGOAdLc7TifzDIvtx6izaRIzg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-transform-react-display-name": "^7.18.6", + "@babel/plugin-transform-react-jsx": "^7.18.6", + "@babel/plugin-transform-react-jsx-development": "^7.18.6", + "@babel/plugin-transform-react-pure-annotations": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-typescript": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.23.3.tgz", + "integrity": "sha512-17oIGVlqz6CchO9RFYn5U6ZpWRZIngayYCtrPRSgANSwC2V1Jb+iP74nVxzzXJte8b8BYxrL1yY96xfhTBrNNQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-option": "^7.22.15", + "@babel/plugin-syntax-jsx": "^7.23.3", + "@babel/plugin-transform-modules-commonjs": "^7.23.3", + "@babel/plugin-transform-typescript": "^7.23.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" + }, + "node_modules/@babel/runtime": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz", + "integrity": "sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/runtime-corejs3": { + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.20.6.tgz", + "integrity": "sha512-tqeujPiuEfcH067mx+7otTQWROVMKHXEaOQcAeNV5dDdbPWvPcFA8/W9LXw2NfjNmOetqLl03dfnG2WALPlsRQ==", + "dependencies": { + "core-js-pure": "^3.25.1", + "regenerator-runtime": "^0.13.11" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/runtime-corejs3/node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + }, + "node_modules/@babel/template": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.23.9.tgz", + "integrity": "sha512-+xrD2BWLpvHKNmX2QbpdpsBaWnRxahMwJjO+KZk2JOElj5nSmKezyS1B4u+QbHMTX69t4ukm6hh9lsYQ7GHCKA==", + "dependencies": { + "@babel/code-frame": "^7.23.5", + "@babel/parser": "^7.23.9", + "@babel/types": "^7.23.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.9.tgz", + "integrity": "sha512-I/4UJ9vs90OkBtY6iiiTORVMyIhJ4kAVmsKo9KFc8UOxMeUfi2hvtIBsET5u9GizXE6/GFSuKCTNfgCswuEjRg==", + "dependencies": { + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.9", + "@babel/types": "^7.23.9", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.5.tgz", + "integrity": "sha512-6mQNsaLeXTw0nxYUYu+NSa4Hx4BlF1x1x8/PMFbiR+GBSr+2DkECc69b8hgy2frEodNcvPffeH8YfWd3LI6jhQ==", + "dependencies": { + "@babel/helper-string-parser": "^7.24.1", + "@babel/helper-validator-identifier": "^7.24.5", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "node_modules/@braintree/sanitize-url": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-6.0.2.tgz", + "integrity": "sha512-Tbsj02wXCbqGmzdnXNk0SOF19ChhRU70BsroIi4Pm6Ehp56in6vch94mfbdQ17DozxkL3BAVjbZ4Qc1a0HFRAg==" + }, + "node_modules/@chevrotain/cst-dts-gen": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-11.0.3.tgz", + "integrity": "sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ==", + "dependencies": { + "@chevrotain/gast": "11.0.3", + "@chevrotain/types": "11.0.3", + "lodash-es": "4.17.21" + } + }, + "node_modules/@chevrotain/gast": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-11.0.3.tgz", + "integrity": "sha512-+qNfcoNk70PyS/uxmj3li5NiECO+2YKZZQMbmjTqRI3Qchu8Hig/Q9vgkHpI3alNjr7M+a2St5pw5w5F6NL5/Q==", + "dependencies": { + "@chevrotain/types": "11.0.3", + "lodash-es": "4.17.21" + } + }, + "node_modules/@chevrotain/regexp-to-ast": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/regexp-to-ast/-/regexp-to-ast-11.0.3.tgz", + "integrity": "sha512-1fMHaBZxLFvWI067AVbGJav1eRY7N8DDvYCTwGBiE/ytKBgP8azTdgyrKyWZ9Mfh09eHWb5PgTSO8wi7U824RA==" + }, + "node_modules/@chevrotain/types": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-11.0.3.tgz", + "integrity": "sha512-gsiM3G8b58kZC2HaWR50gu6Y1440cHiJ+i3JUvcp/35JchYejb2+5MVeJK0iKThYpAa/P2PYFV4hoi44HD+aHQ==" + }, + "node_modules/@chevrotain/utils": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-11.0.3.tgz", + "integrity": "sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ==" + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@docsearch/css": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.5.0.tgz", + "integrity": "sha512-Ob5FQLubplcBNihAVtriR59FRBeP8u69F6mu4L4yIr60KfsPc10bOV0DoPErJw0zF9IBN2cNLW9qdmt8zWPxyg==" + }, + "node_modules/@docsearch/react": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.5.0.tgz", + "integrity": "sha512-3IG8mmSMzSHNGy2S1VuPyYU9tFCxFpj5Ov8SYwsSHM4yMvFsaO9oFxXocA5lSenliIELhuOuS5+BdxHa/Qlf2A==", + "dependencies": { + "@algolia/autocomplete-core": "1.9.2", + "@algolia/autocomplete-preset-algolia": "1.9.2", + "@docsearch/css": "3.5.0", + "algoliasearch": "^4.0.0" + }, + "peerDependencies": { + "@types/react": ">= 16.8.0 < 19.0.0", + "react": ">= 16.8.0 < 19.0.0", + "react-dom": ">= 16.8.0 < 19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/@docusaurus/core": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-2.4.1.tgz", + "integrity": "sha512-SNsY7PshK3Ri7vtsLXVeAJGS50nJN3RgF836zkyUfAD01Fq+sAk5EwWgLw+nnm5KVNGDu7PRR2kRGDsWvqpo0g==", + "dependencies": { + "@babel/core": "^7.18.6", + "@babel/generator": "^7.18.7", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-transform-runtime": "^7.18.6", + "@babel/preset-env": "^7.18.6", + "@babel/preset-react": "^7.18.6", + "@babel/preset-typescript": "^7.18.6", + "@babel/runtime": "^7.18.6", + "@babel/runtime-corejs3": "^7.18.6", + "@babel/traverse": "^7.18.8", + "@docusaurus/cssnano-preset": "2.4.1", + "@docusaurus/logger": "2.4.1", + "@docusaurus/mdx-loader": "2.4.1", + "@docusaurus/react-loadable": "5.5.2", + "@docusaurus/utils": "2.4.1", + "@docusaurus/utils-common": "2.4.1", + "@docusaurus/utils-validation": "2.4.1", + "@slorber/static-site-generator-webpack-plugin": "^4.0.7", + "@svgr/webpack": "^6.2.1", + "autoprefixer": "^10.4.7", + "babel-loader": "^8.2.5", + "babel-plugin-dynamic-import-node": "^2.3.3", + "boxen": "^6.2.1", + "chalk": "^4.1.2", + "chokidar": "^3.5.3", + "clean-css": "^5.3.0", + "cli-table3": "^0.6.2", + "combine-promises": "^1.1.0", + "commander": "^5.1.0", + "copy-webpack-plugin": "^11.0.0", + "core-js": "^3.23.3", + "css-loader": "^6.7.1", + "css-minimizer-webpack-plugin": "^4.0.0", + "cssnano": "^5.1.12", + "del": "^6.1.1", + "detect-port": "^1.3.0", + "escape-html": "^1.0.3", + "eta": "^2.0.0", + "file-loader": "^6.2.0", + "fs-extra": "^10.1.0", + "html-minifier-terser": "^6.1.0", + "html-tags": "^3.2.0", + "html-webpack-plugin": "^5.5.0", + "import-fresh": "^3.3.0", + "leven": "^3.1.0", + "lodash": "^4.17.21", + "mini-css-extract-plugin": "^2.6.1", + "postcss": "^8.4.14", + "postcss-loader": "^7.0.0", + "prompts": "^2.4.2", + "react-dev-utils": "^12.0.1", + "react-helmet-async": "^1.3.0", + "react-loadable": "npm:@docusaurus/react-loadable@5.5.2", + "react-loadable-ssr-addon-v5-slorber": "^1.0.1", + "react-router": "^5.3.3", + "react-router-config": "^5.1.1", + "react-router-dom": "^5.3.3", + "rtl-detect": "^1.0.4", + "semver": "^7.3.7", + "serve-handler": "^6.1.3", + "shelljs": "^0.8.5", + "terser-webpack-plugin": "^5.3.3", + "tslib": "^2.4.0", + "update-notifier": "^5.1.0", + "url-loader": "^4.1.1", + "wait-on": "^6.0.1", + "webpack": "^5.73.0", + "webpack-bundle-analyzer": "^4.5.0", + "webpack-dev-server": "^4.9.3", + "webpack-merge": "^5.8.0", + "webpackbar": "^5.0.2" + }, + "bin": { + "docusaurus": "bin/docusaurus.mjs" + }, + "engines": { + "node": ">=16.14" + }, + "peerDependencies": { + "react": "^16.8.4 || ^17.0.0", + "react-dom": "^16.8.4 || ^17.0.0" + } + }, + "node_modules/@docusaurus/core/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@docusaurus/core/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/@docusaurus/core/node_modules/commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/@docusaurus/core/node_modules/copy-webpack-plugin": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz", + "integrity": "sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==", + "dependencies": { + "fast-glob": "^3.2.11", + "glob-parent": "^6.0.1", + "globby": "^13.1.1", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + } + }, + "node_modules/@docusaurus/core/node_modules/copy-webpack-plugin/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==", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@docusaurus/core/node_modules/css-minimizer-webpack-plugin": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-4.2.2.tgz", + "integrity": "sha512-s3Of/4jKfw1Hj9CxEO1E5oXhQAxlayuHO2y/ML+C6I9sQ7FdzfEV6QgMLN3vI+qFsjJGIAFLKtQK7t8BOXAIyA==", + "dependencies": { + "cssnano": "^5.1.8", + "jest-worker": "^29.1.2", + "postcss": "^8.4.17", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@parcel/css": { + "optional": true + }, + "@swc/css": { + "optional": true + }, + "clean-css": { + "optional": true + }, + "csso": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "lightningcss": { + "optional": true + } + } + }, + "node_modules/@docusaurus/core/node_modules/fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/@docusaurus/core/node_modules/globby": { + "version": "13.1.3", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.3.tgz", + "integrity": "sha512-8krCNHXvlCgHDpegPzleMq07yMYTO2sXKASmZmquEYWEmCx6J5UTRbp5RwMJkTJGtcQ44YpiUYUiN0b9mzy8Bw==", + "dependencies": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.11", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@docusaurus/core/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/@docusaurus/core/node_modules/mini-css-extract-plugin": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.2.tgz", + "integrity": "sha512-EdlUizq13o0Pd+uCp+WO/JpkLvHRVGt97RqfeGhXqAcorYo1ypJSpkV+WDT0vY/kmh/p7wRdJNJtuyK540PXDw==", + "dependencies": { + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/@docusaurus/core/node_modules/postcss-loader": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.0.2.tgz", + "integrity": "sha512-fUJzV/QH7NXUAqV8dWJ9Lg4aTkDCezpTS5HgJ2DvqznexTbSTxgi/dTECvTZ15BwKTtk8G/bqI/QTu2HPd3ZCg==", + "dependencies": { + "cosmiconfig": "^7.0.0", + "klona": "^2.0.5", + "semver": "^7.3.8" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "postcss": "^7.0.0 || ^8.0.1", + "webpack": "^5.0.0" + } + }, + "node_modules/@docusaurus/core/node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/@docusaurus/core/node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@docusaurus/core/node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@docusaurus/cssnano-preset": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-2.4.1.tgz", + "integrity": "sha512-ka+vqXwtcW1NbXxWsh6yA1Ckii1klY9E53cJ4O9J09nkMBgrNX3iEFED1fWdv8wf4mJjvGi5RLZ2p9hJNjsLyQ==", + "dependencies": { + "cssnano-preset-advanced": "^5.3.8", + "postcss": "^8.4.14", + "postcss-sort-media-queries": "^4.2.1", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=16.14" + } + }, + "node_modules/@docusaurus/logger": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-2.4.1.tgz", + "integrity": "sha512-5h5ysIIWYIDHyTVd8BjheZmQZmEgWDR54aQ1BX9pjFfpyzFo5puKXKYrYJXbjEHGyVhEzmB9UXwbxGfaZhOjcg==", + "dependencies": { + "chalk": "^4.1.2", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=16.14" + } + }, + "node_modules/@docusaurus/mdx-loader": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-2.4.1.tgz", + "integrity": "sha512-4KhUhEavteIAmbBj7LVFnrVYDiU51H5YWW1zY6SmBSte/YLhDutztLTBE0PQl1Grux1jzUJeaSvAzHpTn6JJDQ==", + "dependencies": { + "@babel/parser": "^7.18.8", + "@babel/traverse": "^7.18.8", + "@docusaurus/logger": "2.4.1", + "@docusaurus/utils": "2.4.1", + "@mdx-js/mdx": "^1.6.22", + "escape-html": "^1.0.3", + "file-loader": "^6.2.0", + "fs-extra": "^10.1.0", + "image-size": "^1.0.1", + "mdast-util-to-string": "^2.0.0", + "remark-emoji": "^2.2.0", + "stringify-object": "^3.3.0", + "tslib": "^2.4.0", + "unified": "^9.2.2", + "unist-util-visit": "^2.0.3", + "url-loader": "^4.1.1", + "webpack": "^5.73.0" + }, + "engines": { + "node": ">=16.14" + }, + "peerDependencies": { + "react": "^16.8.4 || ^17.0.0", + "react-dom": "^16.8.4 || ^17.0.0" + } + }, + "node_modules/@docusaurus/mdx-loader/node_modules/image-size": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.0.2.tgz", + "integrity": "sha512-xfOoWjceHntRb3qFCrh5ZFORYH8XCdYpASltMhZ/Q0KZiOwjdE/Yl2QCiWdwD+lygV5bMCvauzgu5PxBX/Yerg==", + "dependencies": { + "queue": "6.0.2" + }, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@docusaurus/module-type-aliases": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-2.4.1.tgz", + "integrity": "sha512-gLBuIFM8Dp2XOCWffUDSjtxY7jQgKvYujt7Mx5s4FCTfoL5dN1EVbnrn+O2Wvh8b0a77D57qoIDY7ghgmatR1A==", + "dependencies": { + "@docusaurus/react-loadable": "5.5.2", + "@docusaurus/types": "2.4.1", + "@types/history": "^4.7.11", + "@types/react": "*", + "@types/react-router-config": "*", + "@types/react-router-dom": "*", + "react-helmet-async": "*", + "react-loadable": "npm:@docusaurus/react-loadable@5.5.2" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/@docusaurus/plugin-content-blog": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-2.4.1.tgz", + "integrity": "sha512-E2i7Knz5YIbE1XELI6RlTnZnGgS52cUO4BlCiCUCvQHbR+s1xeIWz4C6BtaVnlug0Ccz7nFSksfwDpVlkujg5Q==", + "dependencies": { + "@docusaurus/core": "2.4.1", + "@docusaurus/logger": "2.4.1", + "@docusaurus/mdx-loader": "2.4.1", + "@docusaurus/types": "2.4.1", + "@docusaurus/utils": "2.4.1", + "@docusaurus/utils-common": "2.4.1", + "@docusaurus/utils-validation": "2.4.1", + "cheerio": "^1.0.0-rc.12", + "feed": "^4.2.2", + "fs-extra": "^10.1.0", + "lodash": "^4.17.21", + "reading-time": "^1.5.0", + "tslib": "^2.4.0", + "unist-util-visit": "^2.0.3", + "utility-types": "^3.10.0", + "webpack": "^5.73.0" + }, + "engines": { + "node": ">=16.14" + }, + "peerDependencies": { + "react": "^16.8.4 || ^17.0.0", + "react-dom": "^16.8.4 || ^17.0.0" + } + }, + "node_modules/@docusaurus/plugin-content-docs": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-2.4.1.tgz", + "integrity": "sha512-Lo7lSIcpswa2Kv4HEeUcGYqaasMUQNpjTXpV0N8G6jXgZaQurqp7E8NGYeGbDXnb48czmHWbzDL4S3+BbK0VzA==", + "dependencies": { + "@docusaurus/core": "2.4.1", + "@docusaurus/logger": "2.4.1", + "@docusaurus/mdx-loader": "2.4.1", + "@docusaurus/module-type-aliases": "2.4.1", + "@docusaurus/types": "2.4.1", + "@docusaurus/utils": "2.4.1", + "@docusaurus/utils-validation": "2.4.1", + "@types/react-router-config": "^5.0.6", + "combine-promises": "^1.1.0", + "fs-extra": "^10.1.0", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "lodash": "^4.17.21", + "tslib": "^2.4.0", + "utility-types": "^3.10.0", + "webpack": "^5.73.0" + }, + "engines": { + "node": ">=16.14" + }, + "peerDependencies": { + "react": "^16.8.4 || ^17.0.0", + "react-dom": "^16.8.4 || ^17.0.0" + } + }, + "node_modules/@docusaurus/plugin-content-docs/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==" + }, + "node_modules/@docusaurus/plugin-content-docs/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==", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@docusaurus/plugin-content-pages": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-2.4.1.tgz", + "integrity": "sha512-/UjuH/76KLaUlL+o1OvyORynv6FURzjurSjvn2lbWTFc4tpYY2qLYTlKpTCBVPhlLUQsfyFnshEJDLmPneq2oA==", + "dependencies": { + "@docusaurus/core": "2.4.1", + "@docusaurus/mdx-loader": "2.4.1", + "@docusaurus/types": "2.4.1", + "@docusaurus/utils": "2.4.1", + "@docusaurus/utils-validation": "2.4.1", + "fs-extra": "^10.1.0", + "tslib": "^2.4.0", + "webpack": "^5.73.0" + }, + "engines": { + "node": ">=16.14" + }, + "peerDependencies": { + "react": "^16.8.4 || ^17.0.0", + "react-dom": "^16.8.4 || ^17.0.0" + } + }, + "node_modules/@docusaurus/plugin-debug": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-2.4.1.tgz", + "integrity": "sha512-7Yu9UPzRShlrH/G8btOpR0e6INFZr0EegWplMjOqelIwAcx3PKyR8mgPTxGTxcqiYj6hxSCRN0D8R7YrzImwNA==", + "dependencies": { + "@docusaurus/core": "2.4.1", + "@docusaurus/types": "2.4.1", + "@docusaurus/utils": "2.4.1", + "fs-extra": "^10.1.0", + "react-json-view": "^1.21.3", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=16.14" + }, + "peerDependencies": { + "react": "^16.8.4 || ^17.0.0", + "react-dom": "^16.8.4 || ^17.0.0" + } + }, + "node_modules/@docusaurus/plugin-google-analytics": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-2.4.1.tgz", + "integrity": "sha512-dyZJdJiCoL+rcfnm0RPkLt/o732HvLiEwmtoNzOoz9MSZz117UH2J6U2vUDtzUzwtFLIf32KkeyzisbwUCgcaQ==", + "dependencies": { + "@docusaurus/core": "2.4.1", + "@docusaurus/types": "2.4.1", + "@docusaurus/utils-validation": "2.4.1", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=16.14" + }, + "peerDependencies": { + "react": "^16.8.4 || ^17.0.0", + "react-dom": "^16.8.4 || ^17.0.0" + } + }, + "node_modules/@docusaurus/plugin-google-gtag": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-2.4.1.tgz", + "integrity": "sha512-mKIefK+2kGTQBYvloNEKtDmnRD7bxHLsBcxgnbt4oZwzi2nxCGjPX6+9SQO2KCN5HZbNrYmGo5GJfMgoRvy6uA==", + "dependencies": { + "@docusaurus/core": "2.4.1", + "@docusaurus/types": "2.4.1", + "@docusaurus/utils-validation": "2.4.1", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=16.14" + }, + "peerDependencies": { + "react": "^16.8.4 || ^17.0.0", + "react-dom": "^16.8.4 || ^17.0.0" + } + }, + "node_modules/@docusaurus/plugin-google-tag-manager": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-2.4.1.tgz", + "integrity": "sha512-Zg4Ii9CMOLfpeV2nG74lVTWNtisFaH9QNtEw48R5QE1KIwDBdTVaiSA18G1EujZjrzJJzXN79VhINSbOJO/r3g==", + "dependencies": { + "@docusaurus/core": "2.4.1", + "@docusaurus/types": "2.4.1", + "@docusaurus/utils-validation": "2.4.1", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=16.14" + }, + "peerDependencies": { + "react": "^16.8.4 || ^17.0.0", + "react-dom": "^16.8.4 || ^17.0.0" + } + }, + "node_modules/@docusaurus/plugin-sitemap": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-2.4.1.tgz", + "integrity": "sha512-lZx+ijt/+atQ3FVE8FOHV/+X3kuok688OydDXrqKRJyXBJZKgGjA2Qa8RjQ4f27V2woaXhtnyrdPop/+OjVMRg==", + "dependencies": { + "@docusaurus/core": "2.4.1", + "@docusaurus/logger": "2.4.1", + "@docusaurus/types": "2.4.1", + "@docusaurus/utils": "2.4.1", + "@docusaurus/utils-common": "2.4.1", + "@docusaurus/utils-validation": "2.4.1", + "fs-extra": "^10.1.0", + "sitemap": "^7.1.1", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=16.14" + }, + "peerDependencies": { + "react": "^16.8.4 || ^17.0.0", + "react-dom": "^16.8.4 || ^17.0.0" + } + }, + "node_modules/@docusaurus/preset-classic": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-2.4.1.tgz", + "integrity": "sha512-P4//+I4zDqQJ+UDgoFrjIFaQ1MeS9UD1cvxVQaI6O7iBmiHQm0MGROP1TbE7HlxlDPXFJjZUK3x3cAoK63smGQ==", + "dependencies": { + "@docusaurus/core": "2.4.1", + "@docusaurus/plugin-content-blog": "2.4.1", + "@docusaurus/plugin-content-docs": "2.4.1", + "@docusaurus/plugin-content-pages": "2.4.1", + "@docusaurus/plugin-debug": "2.4.1", + "@docusaurus/plugin-google-analytics": "2.4.1", + "@docusaurus/plugin-google-gtag": "2.4.1", + "@docusaurus/plugin-google-tag-manager": "2.4.1", + "@docusaurus/plugin-sitemap": "2.4.1", + "@docusaurus/theme-classic": "2.4.1", + "@docusaurus/theme-common": "2.4.1", + "@docusaurus/theme-search-algolia": "2.4.1", + "@docusaurus/types": "2.4.1" + }, + "engines": { + "node": ">=16.14" + }, + "peerDependencies": { + "react": "^16.8.4 || ^17.0.0", + "react-dom": "^16.8.4 || ^17.0.0" + } + }, + "node_modules/@docusaurus/react-loadable": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz", + "integrity": "sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ==", + "dependencies": { + "@types/react": "*", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": "*" + } + }, + "node_modules/@docusaurus/theme-classic": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-2.4.1.tgz", + "integrity": "sha512-Rz0wKUa+LTW1PLXmwnf8mn85EBzaGSt6qamqtmnh9Hflkc+EqiYMhtUJeLdV+wsgYq4aG0ANc+bpUDpsUhdnwg==", + "dependencies": { + "@docusaurus/core": "2.4.1", + "@docusaurus/mdx-loader": "2.4.1", + "@docusaurus/module-type-aliases": "2.4.1", + "@docusaurus/plugin-content-blog": "2.4.1", + "@docusaurus/plugin-content-docs": "2.4.1", + "@docusaurus/plugin-content-pages": "2.4.1", + "@docusaurus/theme-common": "2.4.1", + "@docusaurus/theme-translations": "2.4.1", + "@docusaurus/types": "2.4.1", + "@docusaurus/utils": "2.4.1", + "@docusaurus/utils-common": "2.4.1", + "@docusaurus/utils-validation": "2.4.1", + "@mdx-js/react": "^1.6.22", + "clsx": "^1.2.1", + "copy-text-to-clipboard": "^3.0.1", + "infima": "0.2.0-alpha.43", + "lodash": "^4.17.21", + "nprogress": "^0.2.0", + "postcss": "^8.4.14", + "prism-react-renderer": "^1.3.5", + "prismjs": "^1.28.0", + "react-router-dom": "^5.3.3", + "rtlcss": "^3.5.0", + "tslib": "^2.4.0", + "utility-types": "^3.10.0" + }, + "engines": { + "node": ">=16.14" + }, + "peerDependencies": { + "react": "^16.8.4 || ^17.0.0", + "react-dom": "^16.8.4 || ^17.0.0" + } + }, + "node_modules/@docusaurus/theme-common": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-2.4.1.tgz", + "integrity": "sha512-G7Zau1W5rQTaFFB3x3soQoZpkgMbl/SYNG8PfMFIjKa3M3q8n0m/GRf5/H/e5BqOvt8c+ZWIXGCiz+kUCSHovA==", + "dependencies": { + "@docusaurus/mdx-loader": "2.4.1", + "@docusaurus/module-type-aliases": "2.4.1", + "@docusaurus/plugin-content-blog": "2.4.1", + "@docusaurus/plugin-content-docs": "2.4.1", + "@docusaurus/plugin-content-pages": "2.4.1", + "@docusaurus/utils": "2.4.1", + "@docusaurus/utils-common": "2.4.1", + "@types/history": "^4.7.11", + "@types/react": "*", + "@types/react-router-config": "*", + "clsx": "^1.2.1", + "parse-numeric-range": "^1.3.0", + "prism-react-renderer": "^1.3.5", + "tslib": "^2.4.0", + "use-sync-external-store": "^1.2.0", + "utility-types": "^3.10.0" + }, + "engines": { + "node": ">=16.14" + }, + "peerDependencies": { + "react": "^16.8.4 || ^17.0.0", + "react-dom": "^16.8.4 || ^17.0.0" + } + }, + "node_modules/@docusaurus/theme-mermaid": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-mermaid/-/theme-mermaid-2.4.1.tgz", + "integrity": "sha512-cM0ImKIqZfjmlaC+uAjep39kNBvb1bjz429QBHGs32maob4+UnRzVPPpCUCltyPVb4xjG5h1Tyq4pHzhtIikqA==", + "dependencies": { + "@docusaurus/core": "2.4.1", + "@docusaurus/module-type-aliases": "2.4.1", + "@docusaurus/theme-common": "2.4.1", + "@docusaurus/types": "2.4.1", + "@docusaurus/utils-validation": "2.4.1", + "@mdx-js/react": "^1.6.22", + "mermaid": "^9.2.2", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=16.14" + }, + "peerDependencies": { + "react": "^16.8.4 || ^17.0.0", + "react-dom": "^16.8.4 || ^17.0.0" + } + }, + "node_modules/@docusaurus/theme-search-algolia": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-2.4.1.tgz", + "integrity": "sha512-6BcqW2lnLhZCXuMAvPRezFs1DpmEKzXFKlYjruuas+Xy3AQeFzDJKTJFIm49N77WFCTyxff8d3E4Q9pi/+5McQ==", + "dependencies": { + "@docsearch/react": "^3.1.1", + "@docusaurus/core": "2.4.1", + "@docusaurus/logger": "2.4.1", + "@docusaurus/plugin-content-docs": "2.4.1", + "@docusaurus/theme-common": "2.4.1", + "@docusaurus/theme-translations": "2.4.1", + "@docusaurus/utils": "2.4.1", + "@docusaurus/utils-validation": "2.4.1", + "algoliasearch": "^4.13.1", + "algoliasearch-helper": "^3.10.0", + "clsx": "^1.2.1", + "eta": "^2.0.0", + "fs-extra": "^10.1.0", + "lodash": "^4.17.21", + "tslib": "^2.4.0", + "utility-types": "^3.10.0" + }, + "engines": { + "node": ">=16.14" + }, + "peerDependencies": { + "react": "^16.8.4 || ^17.0.0", + "react-dom": "^16.8.4 || ^17.0.0" + } + }, + "node_modules/@docusaurus/theme-translations": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-2.4.1.tgz", + "integrity": "sha512-T1RAGP+f86CA1kfE8ejZ3T3pUU3XcyvrGMfC/zxCtc2BsnoexuNI9Vk2CmuKCb+Tacvhxjv5unhxXce0+NKyvA==", + "dependencies": { + "fs-extra": "^10.1.0", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=16.14" + } + }, + "node_modules/@docusaurus/types": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-2.4.1.tgz", + "integrity": "sha512-0R+cbhpMkhbRXX138UOc/2XZFF8hiZa6ooZAEEJFp5scytzCw4tC1gChMFXrpa3d2tYE6AX8IrOEpSonLmfQuQ==", + "dependencies": { + "@types/history": "^4.7.11", + "@types/react": "*", + "commander": "^5.1.0", + "joi": "^17.6.0", + "react-helmet-async": "^1.3.0", + "utility-types": "^3.10.0", + "webpack": "^5.73.0", + "webpack-merge": "^5.8.0" + }, + "peerDependencies": { + "react": "^16.8.4 || ^17.0.0", + "react-dom": "^16.8.4 || ^17.0.0" + } + }, + "node_modules/@docusaurus/types/node_modules/commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/@docusaurus/utils": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-2.4.1.tgz", + "integrity": "sha512-1lvEZdAQhKNht9aPXPoh69eeKnV0/62ROhQeFKKxmzd0zkcuE/Oc5Gpnt00y/f5bIsmOsYMY7Pqfm/5rteT5GA==", + "dependencies": { + "@docusaurus/logger": "2.4.1", + "@svgr/webpack": "^6.2.1", + "escape-string-regexp": "^4.0.0", + "file-loader": "^6.2.0", + "fs-extra": "^10.1.0", + "github-slugger": "^1.4.0", + "globby": "^11.1.0", + "gray-matter": "^4.0.3", + "js-yaml": "^4.1.0", + "lodash": "^4.17.21", + "micromatch": "^4.0.5", + "resolve-pathname": "^3.0.0", + "shelljs": "^0.8.5", + "tslib": "^2.4.0", + "url-loader": "^4.1.1", + "webpack": "^5.73.0" + }, + "engines": { + "node": ">=16.14" + }, + "peerDependencies": { + "@docusaurus/types": "*" + }, + "peerDependenciesMeta": { + "@docusaurus/types": { + "optional": true + } + } + }, + "node_modules/@docusaurus/utils-common": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-2.4.1.tgz", + "integrity": "sha512-bCVGdZU+z/qVcIiEQdyx0K13OC5mYwxhSuDUR95oFbKVuXYRrTVrwZIqQljuo1fyJvFTKHiL9L9skQOPokuFNQ==", + "dependencies": { + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=16.14" + }, + "peerDependencies": { + "@docusaurus/types": "*" + }, + "peerDependenciesMeta": { + "@docusaurus/types": { + "optional": true + } + } + }, + "node_modules/@docusaurus/utils-validation": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-2.4.1.tgz", + "integrity": "sha512-unII3hlJlDwZ3w8U+pMO3Lx3RhI4YEbY3YNsQj4yzrkZzlpqZOLuAiZK2JyULnD+TKbceKU0WyWkQXtYbLNDFA==", + "dependencies": { + "@docusaurus/logger": "2.4.1", + "@docusaurus/utils": "2.4.1", + "joi": "^17.6.0", + "js-yaml": "^4.1.0", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=16.14" + } + }, + "node_modules/@docusaurus/utils-validation/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==" + }, + "node_modules/@docusaurus/utils-validation/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==", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@docusaurus/utils/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==" + }, + "node_modules/@docusaurus/utils/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==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@docusaurus/utils/node_modules/fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/@docusaurus/utils/node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@docusaurus/utils/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==", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", + "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", + "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", + "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", + "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", + "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", + "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", + "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", + "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", + "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", + "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", + "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", + "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", + "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", + "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", + "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", + "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", + "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", + "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", + "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", + "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", + "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", + "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", + "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/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": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "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/eslintrc/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.48.0.tgz", + "integrity": "sha512-ZSjtmelB7IJfWD2Fvb7+Z+ChTIKWq6kjda95fLcQKNS5aheVHn4IkfgRQE3sIIzTcSLwLcLZUD9UBt+V7+h+Pw==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@fast-csv/format": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/@fast-csv/format/-/format-4.3.5.tgz", + "integrity": "sha512-8iRn6QF3I8Ak78lNAa+Gdl5MJJBM5vRHivFtMRUWINdevNo00K7OXxS2PshawLKTejVwieIlPmK5YlLu6w4u8A==", + "dependencies": { + "@types/node": "^14.0.1", + "lodash.escaperegexp": "^4.1.2", + "lodash.isboolean": "^3.0.3", + "lodash.isequal": "^4.5.0", + "lodash.isfunction": "^3.0.9", + "lodash.isnil": "^4.0.0" + } + }, + "node_modules/@fast-csv/format/node_modules/@types/node": { + "version": "14.18.33", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.33.tgz", + "integrity": "sha512-qelS/Ra6sacc4loe/3MSjXNL1dNQ/GjxNHVzuChwMfmk7HuycRLVQN2qNY3XahK+fZc5E2szqQSKUyAF0E+2bg==" + }, + "node_modules/@fast-csv/parse": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/@fast-csv/parse/-/parse-4.3.6.tgz", + "integrity": "sha512-uRsLYksqpbDmWaSmzvJcuApSEe38+6NQZBUsuAyMZKqHxH0g1wcJgsKUvN3WC8tewaqFjBMMGrkHmC+T7k8LvA==", + "dependencies": { + "@types/node": "^14.0.1", + "lodash.escaperegexp": "^4.1.2", + "lodash.groupby": "^4.6.0", + "lodash.isfunction": "^3.0.9", + "lodash.isnil": "^4.0.0", + "lodash.isundefined": "^3.0.1", + "lodash.uniq": "^4.5.0" + } + }, + "node_modules/@fast-csv/parse/node_modules/@types/node": { + "version": "14.18.33", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.33.tgz", + "integrity": "sha512-qelS/Ra6sacc4loe/3MSjXNL1dNQ/GjxNHVzuChwMfmk7HuycRLVQN2qNY3XahK+fZc5E2szqQSKUyAF0E+2bg==" + }, + "node_modules/@gar/promisify": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", + "optional": true + }, + "node_modules/@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" + }, + "node_modules/@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", + "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", + "dev": true + }, + "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/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "dependencies": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform/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/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", + "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@jsdoc/salty": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.5.tgz", + "integrity": "sha512-TfRP53RqunNe2HBobVBJ0VLhK1HbfvBYeTC1ahnN64PWvyYyGebmMiPkuwvD9fpw2ZbkoPb8Q7mwy0aR8Z9rvw==", + "dependencies": { + "lodash": "^4.17.21" + }, + "engines": { + "node": ">=v12.0.0" + } + }, + "node_modules/@jvalue/eslint-config-jvalue": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@jvalue/eslint-config-jvalue/-/eslint-config-jvalue-1.3.0.tgz", + "integrity": "sha512-IxPAMboZ8qcpKD07LE6/Q0tKpQUm+1026Y4684G6FfTJ0hNPTLtcnOyLDs47LgNToG93O0pIdnIsbTtgoPdb1Q==", + "dev": true + }, + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", + "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" + }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz", + "integrity": "sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA==", + "dependencies": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@mdx-js/mdx": { + "version": "1.6.22", + "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-1.6.22.tgz", + "integrity": "sha512-AMxuLxPz2j5/6TpF/XSdKpQP1NlG0z11dFOlq+2IP/lSgl11GY8ji6S/rgsViN/L0BDvHvUMruRb7ub+24LUYA==", + "dependencies": { + "@babel/core": "7.12.9", + "@babel/plugin-syntax-jsx": "7.12.1", + "@babel/plugin-syntax-object-rest-spread": "7.8.3", + "@mdx-js/util": "1.6.22", + "babel-plugin-apply-mdx-type-prop": "1.6.22", + "babel-plugin-extract-import-names": "1.6.22", + "camelcase-css": "2.0.1", + "detab": "2.0.4", + "hast-util-raw": "6.0.1", + "lodash.uniq": "4.5.0", + "mdast-util-to-hast": "10.0.1", + "remark-footnotes": "2.0.0", + "remark-mdx": "1.6.22", + "remark-parse": "8.0.3", + "remark-squeeze-paragraphs": "4.0.0", + "style-to-object": "0.3.0", + "unified": "9.2.0", + "unist-builder": "2.0.3", + "unist-util-visit": "2.0.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@mdx-js/mdx/node_modules/@babel/core": { + "version": "7.12.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.9.tgz", + "integrity": "sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ==", + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.12.5", + "@babel/helper-module-transforms": "^7.12.1", + "@babel/helpers": "^7.12.5", + "@babel/parser": "^7.12.7", + "@babel/template": "^7.12.7", + "@babel/traverse": "^7.12.9", + "@babel/types": "^7.12.7", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.2", + "lodash": "^4.17.19", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@mdx-js/mdx/node_modules/@babel/plugin-syntax-jsx": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.1.tgz", + "integrity": "sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@mdx-js/mdx/node_modules/is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "engines": { + "node": ">=4" + } + }, + "node_modules/@mdx-js/mdx/node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@mdx-js/mdx/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/@mdx-js/mdx/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@mdx-js/mdx/node_modules/unified": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.0.tgz", + "integrity": "sha512-vx2Z0vY+a3YoTj8+pttM3tiJHCwY5UFbYdiWrwBEbHmK8pvsPj2rtAX2BFfgXen8T39CJWblWRDT4L5WGXtDdg==", + "dependencies": { + "bail": "^1.0.0", + "extend": "^3.0.0", + "is-buffer": "^2.0.0", + "is-plain-obj": "^2.0.0", + "trough": "^1.0.0", + "vfile": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@mdx-js/react": { + "version": "1.6.22", + "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-1.6.22.tgz", + "integrity": "sha512-TDoPum4SHdfPiGSAaRBw7ECyI8VaHpK8GJugbJIJuqyh6kzw9ZLJZW3HGL3NNrJGxcAixUvqROm+YuQOo5eXtg==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "react": "^16.13.1 || ^17.0.0" + } + }, + "node_modules/@mdx-js/util": { + "version": "1.6.22", + "resolved": "https://registry.npmjs.org/@mdx-js/util/-/util-1.6.22.tgz", + "integrity": "sha512-H1rQc1ZOHANWBvPcW+JpGwr+juXSxM8Q8YCkm3GhZd8REu1fHR3z99CErO1p9pkcfcxZnMdIZdIsXkOHY0NilA==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { + "version": "5.1.1-v1", + "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", + "integrity": "sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==", + "dev": true, + "dependencies": { + "eslint-scope": "5.1.1" + } + }, + "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/@npmcli/fs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", + "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", + "optional": true, + "dependencies": { + "@gar/promisify": "^1.0.1", + "semver": "^7.3.5" + } + }, + "node_modules/@npmcli/fs/node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "optional": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/move-file": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", + "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "deprecated": "This functionality has been moved to @npmcli/fs", + "optional": true, + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@nrwl/devkit": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/@nrwl/devkit/-/devkit-15.3.0.tgz", + "integrity": "sha512-1O9QLB/eYS6ddw4MZnV4yj4CEqLIbpleZZiG/9w1TaiVO/jfNfXVaxc8EA87XSzMpk2W+/4Qggmabt6gAQaabA==", + "dev": true, + "dependencies": { + "@phenomnomnominal/tsquery": "4.1.1", + "ejs": "^3.1.7", + "ignore": "^5.0.4", + "semver": "7.3.4", + "tslib": "^2.3.0" + }, + "peerDependencies": { + "nx": ">= 14 <= 16" + } + }, + "node_modules/@nrwl/esbuild": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nrwl/esbuild/-/esbuild-18.0.1.tgz", + "integrity": "sha512-qUcPnv1q9arcBKSOu0OoBVShmQdEP2p3ejnqFbeiuRxdVhAWzO1j1oGajuer7W37Ii+baoS8OZONdkeYYYCNhg==", + "dev": true, + "dependencies": { + "@nx/esbuild": "18.0.1" + } + }, + "node_modules/@nrwl/eslint-plugin-nx": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nrwl/eslint-plugin-nx/-/eslint-plugin-nx-18.0.1.tgz", + "integrity": "sha512-/E7b09yLtFHf9QCfoqqZpw+HGBnH/Bz1pbZTAvY8zdduhDYO6Em5HRy+Y40pfuWJPUcOgA50j2zNGR+sTCrtRA==", + "dev": true, + "dependencies": { + "@nx/eslint-plugin": "18.0.1" + } + }, + "node_modules/@nrwl/jest": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nrwl/jest/-/jest-18.0.1.tgz", + "integrity": "sha512-j/gd653kPU5qJuYU5RoT2kmy6wmP+GTIL6CtkqlOJ7G38Xbk38JWT+i0fdoAkG5kdsWJIxgDASWxgGocKFOR8w==", + "dev": true, + "dependencies": { + "@nx/jest": "18.0.1" + } + }, + "node_modules/@nrwl/js": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nrwl/js/-/js-18.3.4.tgz", + "integrity": "sha512-oyiMoxzDVGQe5E4UFGO/WAOU211HEIdRxSEOfs1lPhvA8lKbUa0IWDqPOugNws/YHAr+vUTU3sZDJ3uU3RJuYQ==", + "dev": true, + "dependencies": { + "@nx/js": "18.3.4" + } + }, + "node_modules/@nrwl/node": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nrwl/node/-/node-18.0.1.tgz", + "integrity": "sha512-H9wVxteX7vrwIRIOgZgST/eBCBCJIHzVnwmGdI2kGn451zSWpcVDSocD1ANlOhziPqqf1ym4mAtacosjKC1uFw==", + "dev": true, + "dependencies": { + "@nx/node": "18.0.1" + } + }, + "node_modules/@nrwl/react": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nrwl/react/-/react-18.0.1.tgz", + "integrity": "sha512-/YiOdu/GeOd9cXwaY3iZ+sz0m4Dm8L7ZG7YxMK5mcOhIRjyY6JBsig3zBXkAY/JC1IGfXj6pMW54L2F2J43b9g==", + "dev": true, + "dependencies": { + "@nx/react": "18.0.1" + } + }, + "node_modules/@nrwl/rollup": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nrwl/rollup/-/rollup-18.0.1.tgz", + "integrity": "sha512-ObCgvfgbmu13f3EFGqi59mGzHa4WGB9pYQPaYcXy2xLF/kiOhsa5D1fqhYjgbqyHPGOFWdJRsac9qsf1Re/R+w==", + "dev": true, + "dependencies": { + "@nx/rollup": "18.0.1" + } + }, + "node_modules/@nrwl/tao": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nrwl/tao/-/tao-18.0.1.tgz", + "integrity": "sha512-marSAWRyBAiXciwE+893ptwB6kHR+BKxqERBvH6/+2TWhbnOdC8Czf2VnmQIgIjL+bg+76UUZPt5B2r+qGfeAg==", + "dev": true, + "dependencies": { + "nx": "18.0.1", + "tslib": "^2.3.0" + }, + "bin": { + "tao": "index.js" + } + }, + "node_modules/@nrwl/vite": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nrwl/vite/-/vite-18.3.4.tgz", + "integrity": "sha512-0lUQqgui2oWFLUK9bT5XwyyC321tmiKetRxuSvzv+hJZQbMBz6LrYLAWXyEHtLkSVszBqeh8rt8hcrXcIxDfCA==", + "dev": true, + "dependencies": { + "@nx/vite": "18.3.4" + } + }, + "node_modules/@nrwl/web": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nrwl/web/-/web-18.3.4.tgz", + "integrity": "sha512-Zdi0WCAq6+JD1/H8FwzGNjSrHNZWMTN8uPCCV4re3rr2M7oJ+0NqMTATHNqsWGMr2C3HANRuxqC9wDa9Av0XCQ==", + "dev": true, + "dependencies": { + "@nx/web": "18.3.4" + } + }, + "node_modules/@nrwl/webpack": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nrwl/webpack/-/webpack-18.0.1.tgz", + "integrity": "sha512-lVWO7Y8yZi9k1qzJ/Z2bCGPdELlZ9RmlOPzV8yV1Hl9G/cQE8F4W7dUyGe0R9oQNVSwMKbNbHQLvlKOhU+GsBA==", + "dev": true, + "dependencies": { + "@nx/webpack": "18.0.1" + } + }, + "node_modules/@nrwl/workspace": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nrwl/workspace/-/workspace-18.0.1.tgz", + "integrity": "sha512-kkPaSYUAprEWNgXejBgY7DsSn83A6e0wh7jBOY+ou63Al9THWkqYVaAQ/5F8i65HxUlIuCSxhYvKMUoaI/yOLQ==", + "dev": true, + "dependencies": { + "@nx/workspace": "18.0.1" + } + }, + "node_modules/@nx-plus/docusaurus": { + "version": "15.0.0-rc.0", + "resolved": "https://registry.npmjs.org/@nx-plus/docusaurus/-/docusaurus-15.0.0-rc.0.tgz", + "integrity": "sha512-GmoYHKljQJvJLSYhWBe+huStwJbm+RbK+lC2j2sA+ZCUdkUo+P2Yv5oQGbqWU4jXclYe8pOP/qVFJs3j3Zjw/Q==", + "dev": true, + "dependencies": { + "@nrwl/devkit": "^15.0.0" + }, + "peerDependencies": { + "@nrwl/workspace": "^15.0.0" + } + }, + "node_modules/@nx/devkit": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/devkit/-/devkit-18.0.1.tgz", + "integrity": "sha512-YmX5YqZAGqIK6ACwj6+BmogbOr/HVrILnx5ybiHPwNzMTv5HQpJ67HfBRYUDKIFiY4zTjciYyWAmqG89UYCq1w==", + "dev": true, + "dependencies": { + "@nrwl/devkit": "18.0.1", + "ejs": "^3.1.7", + "enquirer": "~2.3.6", + "ignore": "^5.0.4", + "semver": "^7.5.3", + "tmp": "~0.2.1", + "tslib": "^2.3.0", + "yargs-parser": "21.1.1" + }, + "peerDependencies": { + "nx": ">= 16 <= 18" + } + }, + "node_modules/@nx/devkit/node_modules/@nrwl/devkit": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nrwl/devkit/-/devkit-18.0.1.tgz", + "integrity": "sha512-9SXlKk+LSNMVaKE6RqjIZ2agPKQVljOrt2cMyQe0SQCsqxrzqajwGD19zDGISiOLiiq24QPz30Q+vM7W98Yb9g==", + "dev": true, + "dependencies": { + "@nx/devkit": "18.0.1" + } + }, + "node_modules/@nx/devkit/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@nx/esbuild": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/esbuild/-/esbuild-18.0.1.tgz", + "integrity": "sha512-a/SC22lRW6VKPngkYemQ2ZP5eiG0OBCWSYikyZi047vKQX2OM01uqRHzsspzCU0gJ7rl0Sb/xjH1L+PSPBEXRg==", + "dev": true, + "dependencies": { + "@nrwl/esbuild": "18.0.1", + "@nx/devkit": "18.0.1", + "@nx/js": "18.0.1", + "chalk": "^4.1.0", + "fast-glob": "3.2.7", + "fs-extra": "^11.1.0", + "tsconfig-paths": "^4.1.2", + "tslib": "^2.3.0" + }, + "peerDependencies": { + "esbuild": "~0.19.2" + }, + "peerDependenciesMeta": { + "esbuild": { + "optional": true + } + } + }, + "node_modules/@nx/esbuild/node_modules/@nrwl/js": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nrwl/js/-/js-18.0.1.tgz", + "integrity": "sha512-O1wfFdDMvObgqZgH/5qebm9YeN5cJ+UL2fJV2ZnsWVz3e8jhOQLflN3J7jXUWxzsz5stYiplsGpBjgXQRcNwaQ==", + "dev": true, + "dependencies": { + "@nx/js": "18.0.1" + } + }, + "node_modules/@nx/esbuild/node_modules/@nx/js": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/js/-/js-18.0.1.tgz", + "integrity": "sha512-U/l8K09UMrE4zVXEpmwpRWaY4fCrdcaaB3NXsEUwghJE6OuacADdaQ5ZWPxa0ji3nARKW7Umj87sOlUu8YjpTQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.23.2", + "@babel/plugin-proposal-decorators": "^7.22.7", + "@babel/plugin-transform-class-properties": "^7.22.5", + "@babel/plugin-transform-runtime": "^7.23.2", + "@babel/preset-env": "^7.23.2", + "@babel/preset-typescript": "^7.22.5", + "@babel/runtime": "^7.22.6", + "@nrwl/js": "18.0.1", + "@nx/devkit": "18.0.1", + "@nx/workspace": "18.0.1", + "@phenomnomnominal/tsquery": "~5.0.1", + "babel-plugin-const-enum": "^1.0.1", + "babel-plugin-macros": "^2.8.0", + "babel-plugin-transform-typescript-metadata": "^0.3.1", + "chalk": "^4.1.0", + "columnify": "^1.6.0", + "detect-port": "^1.5.1", + "fast-glob": "3.2.7", + "fs-extra": "^11.1.0", + "ignore": "^5.0.4", + "js-tokens": "^4.0.0", + "minimatch": "9.0.3", + "npm-package-arg": "11.0.1", + "npm-run-path": "^4.0.1", + "ora": "5.3.0", + "semver": "^7.5.3", + "source-map-support": "0.5.19", + "ts-node": "10.9.1", + "tsconfig-paths": "^4.1.2", + "tslib": "^2.3.0" + }, + "peerDependencies": { + "verdaccio": "^5.0.4" + }, + "peerDependenciesMeta": { + "verdaccio": { + "optional": true + } + } + }, + "node_modules/@nx/esbuild/node_modules/@phenomnomnominal/tsquery": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@phenomnomnominal/tsquery/-/tsquery-5.0.1.tgz", + "integrity": "sha512-3nVv+e2FQwsW8Aw6qTU6f+1rfcJ3hrcnvH/mu9i8YhxO+9sqbOfpL8m6PbET5+xKOlz/VSbp0RoYWYCtIsnmuA==", + "dev": true, + "dependencies": { + "esquery": "^1.4.0" + }, + "peerDependencies": { + "typescript": "^3 || ^4 || ^5" + } + }, + "node_modules/@nx/esbuild/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@nx/esbuild/node_modules/fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@nx/esbuild/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@nx/esbuild/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@nx/esbuild/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@nx/esbuild/node_modules/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "dependencies": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@nx/eslint": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/eslint/-/eslint-18.0.1.tgz", + "integrity": "sha512-0IZOFt/58f47TC2atMa1ClmA3lKrflnG8YEJznvy86iwHMARxCSxsBEK+DKxMSeFMKBbxuC26aN+9MweFGohKA==", + "dev": true, + "dependencies": { + "@nx/devkit": "18.0.1", + "@nx/js": "18.0.1", + "@nx/linter": "18.0.1", + "eslint": "^8.0.0", + "tslib": "^2.3.0", + "typescript": "~5.3.2" + }, + "peerDependencies": { + "js-yaml": "4.1.0" + }, + "peerDependenciesMeta": { + "js-yaml": { + "optional": true + } + } + }, + "node_modules/@nx/eslint-plugin": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/eslint-plugin/-/eslint-plugin-18.0.1.tgz", + "integrity": "sha512-RGFGKZWh3EsRq+vJjdsFk5zkBLSZJrYoMQpZufudHcT2+WY61UkbVOdWTwG0GdegcPCDU9+UztDAH3HXCOeQaw==", + "dev": true, + "dependencies": { + "@nrwl/eslint-plugin-nx": "18.0.1", + "@nx/devkit": "18.0.1", + "@nx/js": "18.0.1", + "@typescript-eslint/type-utils": "^6.13.2", + "@typescript-eslint/utils": "^6.13.2", + "chalk": "^4.1.0", + "confusing-browser-globals": "^1.0.9", + "jsonc-eslint-parser": "^2.1.0", + "semver": "^7.5.3", + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^6.13.2", + "eslint-config-prettier": "^9.0.0" + }, + "peerDependenciesMeta": { + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/@nx/eslint-plugin/node_modules/@nrwl/js": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nrwl/js/-/js-18.0.1.tgz", + "integrity": "sha512-O1wfFdDMvObgqZgH/5qebm9YeN5cJ+UL2fJV2ZnsWVz3e8jhOQLflN3J7jXUWxzsz5stYiplsGpBjgXQRcNwaQ==", + "dev": true, + "dependencies": { + "@nx/js": "18.0.1" + } + }, + "node_modules/@nx/eslint-plugin/node_modules/@nx/js": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/js/-/js-18.0.1.tgz", + "integrity": "sha512-U/l8K09UMrE4zVXEpmwpRWaY4fCrdcaaB3NXsEUwghJE6OuacADdaQ5ZWPxa0ji3nARKW7Umj87sOlUu8YjpTQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.23.2", + "@babel/plugin-proposal-decorators": "^7.22.7", + "@babel/plugin-transform-class-properties": "^7.22.5", + "@babel/plugin-transform-runtime": "^7.23.2", + "@babel/preset-env": "^7.23.2", + "@babel/preset-typescript": "^7.22.5", + "@babel/runtime": "^7.22.6", + "@nrwl/js": "18.0.1", + "@nx/devkit": "18.0.1", + "@nx/workspace": "18.0.1", + "@phenomnomnominal/tsquery": "~5.0.1", + "babel-plugin-const-enum": "^1.0.1", + "babel-plugin-macros": "^2.8.0", + "babel-plugin-transform-typescript-metadata": "^0.3.1", + "chalk": "^4.1.0", + "columnify": "^1.6.0", + "detect-port": "^1.5.1", + "fast-glob": "3.2.7", + "fs-extra": "^11.1.0", + "ignore": "^5.0.4", + "js-tokens": "^4.0.0", + "minimatch": "9.0.3", + "npm-package-arg": "11.0.1", + "npm-run-path": "^4.0.1", + "ora": "5.3.0", + "semver": "^7.5.3", + "source-map-support": "0.5.19", + "ts-node": "10.9.1", + "tsconfig-paths": "^4.1.2", + "tslib": "^2.3.0" + }, + "peerDependencies": { + "verdaccio": "^5.0.4" + }, + "peerDependenciesMeta": { + "verdaccio": { + "optional": true + } + } + }, + "node_modules/@nx/eslint-plugin/node_modules/@nx/js/node_modules/fast-glob": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", + "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@nx/eslint-plugin/node_modules/@phenomnomnominal/tsquery": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@phenomnomnominal/tsquery/-/tsquery-5.0.1.tgz", + "integrity": "sha512-3nVv+e2FQwsW8Aw6qTU6f+1rfcJ3hrcnvH/mu9i8YhxO+9sqbOfpL8m6PbET5+xKOlz/VSbp0RoYWYCtIsnmuA==", + "dev": true, + "dependencies": { + "esquery": "^1.4.0" + }, + "peerDependencies": { + "typescript": "^3 || ^4 || ^5" + } + }, + "node_modules/@nx/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.20.0.tgz", + "integrity": "sha512-p4rvHQRDTI1tGGMDFQm+GtxP1ZHyAh64WANVoyEcNMpaTFn3ox/3CcgtIlELnRfKzSs/DwYlDccJEtr3O6qBvA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.20.0", + "@typescript-eslint/visitor-keys": "6.20.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@nx/eslint-plugin/node_modules/@typescript-eslint/types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.20.0.tgz", + "integrity": "sha512-MM9mfZMAhiN4cOEcUOEx+0HmuaW3WBfukBZPCfwSqFnQy0grXYtngKCqpQN339X3RrwtzspWJrpbrupKYUSBXQ==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@nx/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.20.0.tgz", + "integrity": "sha512-RnRya9q5m6YYSpBN7IzKu9FmLcYtErkDkc8/dKv81I9QiLLtVBHrjz+Ev/crAqgMNW2FCsoZF4g2QUylMnJz+g==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.20.0", + "@typescript-eslint/visitor-keys": "6.20.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@nx/eslint-plugin/node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@nx/eslint-plugin/node_modules/@typescript-eslint/utils": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.20.0.tgz", + "integrity": "sha512-/EKuw+kRu2vAqCoDwDCBtDRU6CTKbUmwwI7SH7AashZ+W+7o8eiyy6V2cdOqN49KsTcASWsC5QeghYuRDTyOOg==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.20.0", + "@typescript-eslint/types": "6.20.0", + "@typescript-eslint/typescript-estree": "6.20.0", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/@nx/eslint-plugin/node_modules/@typescript-eslint/utils/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@nx/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.20.0.tgz", + "integrity": "sha512-E8Cp98kRe4gKHjJD4NExXKz/zOJ1A2hhZc+IMVD6i7w4yjIvh6VyuRI0gRtxAsXtoC35uGMaQ9rjI2zJaXDEAw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.20.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@nx/eslint-plugin/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@nx/eslint-plugin/node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/@nx/eslint-plugin/node_modules/fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@nx/eslint-plugin/node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@nx/eslint-plugin/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@nx/eslint-plugin/node_modules/semver": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", + "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@nx/eslint-plugin/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@nx/eslint-plugin/node_modules/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "dependencies": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@nx/eslint/node_modules/@nrwl/js": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nrwl/js/-/js-18.0.1.tgz", + "integrity": "sha512-O1wfFdDMvObgqZgH/5qebm9YeN5cJ+UL2fJV2ZnsWVz3e8jhOQLflN3J7jXUWxzsz5stYiplsGpBjgXQRcNwaQ==", + "dev": true, + "dependencies": { + "@nx/js": "18.0.1" + } + }, + "node_modules/@nx/eslint/node_modules/@nx/js": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/js/-/js-18.0.1.tgz", + "integrity": "sha512-U/l8K09UMrE4zVXEpmwpRWaY4fCrdcaaB3NXsEUwghJE6OuacADdaQ5ZWPxa0ji3nARKW7Umj87sOlUu8YjpTQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.23.2", + "@babel/plugin-proposal-decorators": "^7.22.7", + "@babel/plugin-transform-class-properties": "^7.22.5", + "@babel/plugin-transform-runtime": "^7.23.2", + "@babel/preset-env": "^7.23.2", + "@babel/preset-typescript": "^7.22.5", + "@babel/runtime": "^7.22.6", + "@nrwl/js": "18.0.1", + "@nx/devkit": "18.0.1", + "@nx/workspace": "18.0.1", + "@phenomnomnominal/tsquery": "~5.0.1", + "babel-plugin-const-enum": "^1.0.1", + "babel-plugin-macros": "^2.8.0", + "babel-plugin-transform-typescript-metadata": "^0.3.1", + "chalk": "^4.1.0", + "columnify": "^1.6.0", + "detect-port": "^1.5.1", + "fast-glob": "3.2.7", + "fs-extra": "^11.1.0", + "ignore": "^5.0.4", + "js-tokens": "^4.0.0", + "minimatch": "9.0.3", + "npm-package-arg": "11.0.1", + "npm-run-path": "^4.0.1", + "ora": "5.3.0", + "semver": "^7.5.3", + "source-map-support": "0.5.19", + "ts-node": "10.9.1", + "tsconfig-paths": "^4.1.2", + "tslib": "^2.3.0" + }, + "peerDependencies": { + "verdaccio": "^5.0.4" + }, + "peerDependenciesMeta": { + "verdaccio": { + "optional": true + } + } + }, + "node_modules/@nx/eslint/node_modules/@phenomnomnominal/tsquery": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@phenomnomnominal/tsquery/-/tsquery-5.0.1.tgz", + "integrity": "sha512-3nVv+e2FQwsW8Aw6qTU6f+1rfcJ3hrcnvH/mu9i8YhxO+9sqbOfpL8m6PbET5+xKOlz/VSbp0RoYWYCtIsnmuA==", + "dev": true, + "dependencies": { + "esquery": "^1.4.0" + }, + "peerDependencies": { + "typescript": "^3 || ^4 || ^5" + } + }, + "node_modules/@nx/eslint/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@nx/eslint/node_modules/fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@nx/eslint/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@nx/eslint/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@nx/eslint/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@nx/eslint/node_modules/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "dependencies": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@nx/jest": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/jest/-/jest-18.0.1.tgz", + "integrity": "sha512-ILBur8l4pKxpBCPQzIooPxGpV2NcwK2qLydskZhxyu+vm36kp3py5CyKo+j8HLlskJ7M6QGZf08l3DgsG+CYbQ==", + "dev": true, + "dependencies": { + "@jest/reporters": "^29.4.1", + "@jest/test-result": "^29.4.1", + "@nrwl/jest": "18.0.1", + "@nx/devkit": "18.0.1", + "@nx/js": "18.0.1", + "@phenomnomnominal/tsquery": "~5.0.1", + "chalk": "^4.1.0", + "identity-obj-proxy": "3.0.0", + "jest-config": "^29.4.1", + "jest-resolve": "^29.4.1", + "jest-util": "^29.4.1", + "minimatch": "9.0.3", + "resolve.exports": "1.1.0", + "tslib": "^2.3.0" + } + }, + "node_modules/@nx/jest/node_modules/@nrwl/js": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nrwl/js/-/js-18.0.1.tgz", + "integrity": "sha512-O1wfFdDMvObgqZgH/5qebm9YeN5cJ+UL2fJV2ZnsWVz3e8jhOQLflN3J7jXUWxzsz5stYiplsGpBjgXQRcNwaQ==", + "dev": true, + "dependencies": { + "@nx/js": "18.0.1" + } + }, + "node_modules/@nx/jest/node_modules/@nx/js": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/js/-/js-18.0.1.tgz", + "integrity": "sha512-U/l8K09UMrE4zVXEpmwpRWaY4fCrdcaaB3NXsEUwghJE6OuacADdaQ5ZWPxa0ji3nARKW7Umj87sOlUu8YjpTQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.23.2", + "@babel/plugin-proposal-decorators": "^7.22.7", + "@babel/plugin-transform-class-properties": "^7.22.5", + "@babel/plugin-transform-runtime": "^7.23.2", + "@babel/preset-env": "^7.23.2", + "@babel/preset-typescript": "^7.22.5", + "@babel/runtime": "^7.22.6", + "@nrwl/js": "18.0.1", + "@nx/devkit": "18.0.1", + "@nx/workspace": "18.0.1", + "@phenomnomnominal/tsquery": "~5.0.1", + "babel-plugin-const-enum": "^1.0.1", + "babel-plugin-macros": "^2.8.0", + "babel-plugin-transform-typescript-metadata": "^0.3.1", + "chalk": "^4.1.0", + "columnify": "^1.6.0", + "detect-port": "^1.5.1", + "fast-glob": "3.2.7", + "fs-extra": "^11.1.0", + "ignore": "^5.0.4", + "js-tokens": "^4.0.0", + "minimatch": "9.0.3", + "npm-package-arg": "11.0.1", + "npm-run-path": "^4.0.1", + "ora": "5.3.0", + "semver": "^7.5.3", + "source-map-support": "0.5.19", + "ts-node": "10.9.1", + "tsconfig-paths": "^4.1.2", + "tslib": "^2.3.0" + }, + "peerDependencies": { + "verdaccio": "^5.0.4" + }, + "peerDependenciesMeta": { + "verdaccio": { + "optional": true + } + } + }, + "node_modules/@nx/jest/node_modules/@phenomnomnominal/tsquery": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@phenomnomnominal/tsquery/-/tsquery-5.0.1.tgz", + "integrity": "sha512-3nVv+e2FQwsW8Aw6qTU6f+1rfcJ3hrcnvH/mu9i8YhxO+9sqbOfpL8m6PbET5+xKOlz/VSbp0RoYWYCtIsnmuA==", + "dev": true, + "dependencies": { + "esquery": "^1.4.0" + }, + "peerDependencies": { + "typescript": "^3 || ^4 || ^5" + } + }, + "node_modules/@nx/jest/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@nx/jest/node_modules/fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@nx/jest/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@nx/jest/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@nx/jest/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@nx/jest/node_modules/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "dependencies": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@nx/js": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nx/js/-/js-18.3.4.tgz", + "integrity": "sha512-+MPacp/B09e5QwaFQBkev9pW862ZpmesqR2lUUnFAXUBKtjYVIAmhJWHOtevqC1om4OxvTsbluYHsbAkAUzlMA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.23.2", + "@babel/plugin-proposal-decorators": "^7.22.7", + "@babel/plugin-transform-class-properties": "^7.22.5", + "@babel/plugin-transform-runtime": "^7.23.2", + "@babel/preset-env": "^7.23.2", + "@babel/preset-typescript": "^7.22.5", + "@babel/runtime": "^7.22.6", + "@nrwl/js": "18.3.4", + "@nx/devkit": "18.3.4", + "@nx/workspace": "18.3.4", + "@phenomnomnominal/tsquery": "~5.0.1", + "babel-plugin-const-enum": "^1.0.1", + "babel-plugin-macros": "^2.8.0", + "babel-plugin-transform-typescript-metadata": "^0.3.1", + "chalk": "^4.1.0", + "columnify": "^1.6.0", + "detect-port": "^1.5.1", + "fast-glob": "3.2.7", + "fs-extra": "^11.1.0", + "ignore": "^5.0.4", + "js-tokens": "^4.0.0", + "minimatch": "9.0.3", + "npm-package-arg": "11.0.1", + "npm-run-path": "^4.0.1", + "ora": "5.3.0", + "semver": "^7.5.3", + "source-map-support": "0.5.19", + "ts-node": "10.9.1", + "tsconfig-paths": "^4.1.2", + "tslib": "^2.3.0" + }, + "peerDependencies": { + "verdaccio": "^5.0.4" + }, + "peerDependenciesMeta": { + "verdaccio": { + "optional": true + } + } + }, + "node_modules/@nx/js/node_modules/@nrwl/devkit": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nrwl/devkit/-/devkit-18.3.4.tgz", + "integrity": "sha512-Fty9Huqm12OYueU3uLJl3uvBUl5BvEyPfvw8+rLiNx9iftdEattM8C+268eAbIRRSLSOVXlWsJH4brlc6QZYYw==", + "dev": true, + "dependencies": { + "@nx/devkit": "18.3.4" + } + }, + "node_modules/@nx/js/node_modules/@nrwl/tao": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nrwl/tao/-/tao-18.3.4.tgz", + "integrity": "sha512-+7KsDYmGj1cvNaXZcjSYOPN1h17hsGFBtVX7MqnpJLLkQTUhKg2rQxqyluzshJ+RoDUVtYPGyHg1AizlB66RIA==", + "dev": true, + "dependencies": { + "nx": "18.3.4", + "tslib": "^2.3.0" + }, + "bin": { + "tao": "index.js" + } + }, + "node_modules/@nx/js/node_modules/@nrwl/workspace": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nrwl/workspace/-/workspace-18.3.4.tgz", + "integrity": "sha512-ziPHZcSYj46aPYrRHaKu56/SmYCijLT5vIm/UaoWD5v5Fy5CRigO/ezUImsHGHMEZWfHt44s4jsv7QdJWAXe7w==", + "dev": true, + "dependencies": { + "@nx/workspace": "18.3.4" + } + }, + "node_modules/@nx/js/node_modules/@nx/devkit": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nx/devkit/-/devkit-18.3.4.tgz", + "integrity": "sha512-M3htxl5WvlNKK5KNOndCAApbyBCZNTFFs+rtdwvudNZk5+84zAAPaWzSoX9C4XLAW78/f98LzF68/ch05aN12A==", + "dev": true, + "dependencies": { + "@nrwl/devkit": "18.3.4", + "ejs": "^3.1.7", + "enquirer": "~2.3.6", + "ignore": "^5.0.4", + "semver": "^7.5.3", + "tmp": "~0.2.1", + "tslib": "^2.3.0", + "yargs-parser": "21.1.1" + }, + "peerDependencies": { + "nx": ">= 16 <= 19" + } + }, + "node_modules/@nx/js/node_modules/@nx/nx-darwin-arm64": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nx/nx-darwin-arm64/-/nx-darwin-arm64-18.3.4.tgz", + "integrity": "sha512-MOGk9z4fIoOkJB68diH3bwoWrC8X9IzMNsz1mu0cbVfgCRAfIV3b+lMsiwQYzWal3UWW5DE5Rkss4F8whiV5Uw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nx/js/node_modules/@nx/nx-darwin-x64": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nx/nx-darwin-x64/-/nx-darwin-x64-18.3.4.tgz", + "integrity": "sha512-tSzPRnNB3QdPM+KYiIuRCUtyCwcuIRC95FfP0ZB3WvfDeNxJChEAChNqmCMDE4iFvZhGuze8WqkJuIVdte+lyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nx/js/node_modules/@nx/nx-freebsd-x64": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nx/nx-freebsd-x64/-/nx-freebsd-x64-18.3.4.tgz", + "integrity": "sha512-bjSPak/d+bcR95/pxHMRhnnpHc6MnrQcG6f5AjX15Esm4JdrdQKPBmG1RybuK0WKSyD5wgVhkAGc/QQUom9l8g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nx/js/node_modules/@nx/nx-linux-arm-gnueabihf": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-18.3.4.tgz", + "integrity": "sha512-/1HnUL7jhH0S7PxJqf6R1pk3QlAU22GY89EQV9fd+RDUtp7IyzaTlkebijTIqfxlSjC4OO3bPizaxEaxdd3uKQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nx/js/node_modules/@nx/nx-linux-arm64-gnu": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-18.3.4.tgz", + "integrity": "sha512-g/2IaB2bZTKaBNPEf9LxtIXb1XHdhh3VO9PnePIrwkkixPMLN0dTxT5Sttt75lvLP3EU1AUR5w3Aaz2Q1mYtWA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nx/js/node_modules/@nx/nx-linux-arm64-musl": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-18.3.4.tgz", + "integrity": "sha512-MgfKLoEF6I1cCS+0ooFLEjJSSVdCYyCT9Q96IHRJntAEL8u/0GR2OUoBoLC+q1lnbIkJr/uqTJxA2Jh+sJTIbA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nx/js/node_modules/@nx/nx-linux-x64-gnu": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-18.3.4.tgz", + "integrity": "sha512-vbHxv7m3gjthBvw50EYCtgyY0Zg5nVTaQtX+wRsmKybV2i7wHbw5zIe1aL4zHUm6TcPGbIQK+utVM+hyCqKHVA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nx/js/node_modules/@nx/nx-linux-x64-musl": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-18.3.4.tgz", + "integrity": "sha512-qIJKJCYFRLVSALsvg3avjReOjuYk91Q0hFXMJ2KaEM1Y3tdzcFN0fKBiaHexgbFIUk8zJuS4dJObTqSYMXowbg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nx/js/node_modules/@nx/nx-win32-arm64-msvc": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-18.3.4.tgz", + "integrity": "sha512-UxC8mRkFTPdZbKFprZkiBqVw8624xU38kI0xyooxKlFpt5lccTBwJ0B7+R8p1RoWyvh2DSyFI9VvfD7lczg1lA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nx/js/node_modules/@nx/nx-win32-x64-msvc": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-18.3.4.tgz", + "integrity": "sha512-/RqEjNU9hxIBxRLafCNKoH3SaB2FShf+1ZnIYCdAoCZBxLJebDpnhiyrVs0lPnMj9248JbizEMdJj1+bs/bXig==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nx/js/node_modules/@nx/workspace": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nx/workspace/-/workspace-18.3.4.tgz", + "integrity": "sha512-H5HmEOWb9wnrNXfI2DhK6AmMVz1snuJvjT2jcMf9kxlVW0pKGTFW+OyHfSYq6Ni3OGWb1f9O63erLYHo45zPeA==", + "dev": true, + "dependencies": { + "@nrwl/workspace": "18.3.4", + "@nx/devkit": "18.3.4", + "chalk": "^4.1.0", + "enquirer": "~2.3.6", + "nx": "18.3.4", + "tslib": "^2.3.0", + "yargs-parser": "21.1.1" + } + }, + "node_modules/@nx/js/node_modules/@phenomnomnominal/tsquery": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@phenomnomnominal/tsquery/-/tsquery-5.0.1.tgz", + "integrity": "sha512-3nVv+e2FQwsW8Aw6qTU6f+1rfcJ3hrcnvH/mu9i8YhxO+9sqbOfpL8m6PbET5+xKOlz/VSbp0RoYWYCtIsnmuA==", + "dev": true, + "dependencies": { + "esquery": "^1.4.0" + }, + "peerDependencies": { + "typescript": "^3 || ^4 || ^5" + } + }, + "node_modules/@nx/js/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/@nx/js/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@nx/js/node_modules/fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@nx/js/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/@nx/js/node_modules/lines-and-columns": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-2.0.4.tgz", + "integrity": "sha512-wM1+Z03eypVAVUCE7QdSqpVIvelbOakn1M0bPDoA4SGWPx3sNDVUiMo3L6To6WWGClB7VyXnhQ4Sn7gxiJbE6A==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/@nx/js/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@nx/js/node_modules/nx": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/nx/-/nx-18.3.4.tgz", + "integrity": "sha512-7rOHRyxpnZGJ3pHnwmpoAMHt9hNuwibWhOhPBJDhJVcbQJtGfwcWWyV/iSEnVXwKZ2lfHVE3TwE+gXFdT/GFiw==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@nrwl/tao": "18.3.4", + "@yarnpkg/lockfile": "^1.1.0", + "@yarnpkg/parsers": "3.0.0-rc.46", + "@zkochan/js-yaml": "0.0.6", + "axios": "^1.6.0", + "chalk": "^4.1.0", + "cli-cursor": "3.1.0", + "cli-spinners": "2.6.1", + "cliui": "^8.0.1", + "dotenv": "~16.3.1", + "dotenv-expand": "~10.0.0", + "enquirer": "~2.3.6", + "figures": "3.2.0", + "flat": "^5.0.2", + "fs-extra": "^11.1.0", + "ignore": "^5.0.4", + "jest-diff": "^29.4.1", + "js-yaml": "4.1.0", + "jsonc-parser": "3.2.0", + "lines-and-columns": "~2.0.3", + "minimatch": "9.0.3", + "node-machine-id": "1.1.12", + "npm-run-path": "^4.0.1", + "open": "^8.4.0", + "ora": "5.3.0", + "semver": "^7.5.3", + "string-width": "^4.2.3", + "strong-log-transformer": "^2.1.0", + "tar-stream": "~2.2.0", + "tmp": "~0.2.1", + "tsconfig-paths": "^4.1.2", + "tslib": "^2.3.0", + "yargs": "^17.6.2", + "yargs-parser": "21.1.1" + }, + "bin": { + "nx": "bin/nx.js", + "nx-cloud": "bin/nx-cloud.js" + }, + "optionalDependencies": { + "@nx/nx-darwin-arm64": "18.3.4", + "@nx/nx-darwin-x64": "18.3.4", + "@nx/nx-freebsd-x64": "18.3.4", + "@nx/nx-linux-arm-gnueabihf": "18.3.4", + "@nx/nx-linux-arm64-gnu": "18.3.4", + "@nx/nx-linux-arm64-musl": "18.3.4", + "@nx/nx-linux-x64-gnu": "18.3.4", + "@nx/nx-linux-x64-musl": "18.3.4", + "@nx/nx-win32-arm64-msvc": "18.3.4", + "@nx/nx-win32-x64-msvc": "18.3.4" + }, + "peerDependencies": { + "@swc-node/register": "^1.8.0", + "@swc/core": "^1.3.85" + }, + "peerDependenciesMeta": { + "@swc-node/register": { + "optional": true + }, + "@swc/core": { + "optional": true + } + } + }, + "node_modules/@nx/js/node_modules/semver": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", + "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@nx/js/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@nx/js/node_modules/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "dependencies": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@nx/linter": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/linter/-/linter-18.0.1.tgz", + "integrity": "sha512-RE24Jjdi3R56g9r05hOe37oXx8d5VkVvUC/njgxdaVYy569lgPdiXD9FHaHBXOwjQ3JF7ItVOd2zNhTiZA1S4Q==", + "dev": true, + "dependencies": { + "@nx/eslint": "18.0.1" + } + }, + "node_modules/@nx/node": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/node/-/node-18.0.1.tgz", + "integrity": "sha512-SG9RapK6OQsOpv+LdD+7UErmN7jjc4WJHoIRjE3NCJQVJYP2zLaus8kfdSBjF6x+jx/y9fyn/Bxhtznp1IjeLw==", + "dev": true, + "dependencies": { + "@nrwl/node": "18.0.1", + "@nx/devkit": "18.0.1", + "@nx/eslint": "18.0.1", + "@nx/jest": "18.0.1", + "@nx/js": "18.0.1", + "tslib": "^2.3.0" + } + }, + "node_modules/@nx/node/node_modules/@nrwl/js": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nrwl/js/-/js-18.0.1.tgz", + "integrity": "sha512-O1wfFdDMvObgqZgH/5qebm9YeN5cJ+UL2fJV2ZnsWVz3e8jhOQLflN3J7jXUWxzsz5stYiplsGpBjgXQRcNwaQ==", + "dev": true, + "dependencies": { + "@nx/js": "18.0.1" + } + }, + "node_modules/@nx/node/node_modules/@nx/js": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/js/-/js-18.0.1.tgz", + "integrity": "sha512-U/l8K09UMrE4zVXEpmwpRWaY4fCrdcaaB3NXsEUwghJE6OuacADdaQ5ZWPxa0ji3nARKW7Umj87sOlUu8YjpTQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.23.2", + "@babel/plugin-proposal-decorators": "^7.22.7", + "@babel/plugin-transform-class-properties": "^7.22.5", + "@babel/plugin-transform-runtime": "^7.23.2", + "@babel/preset-env": "^7.23.2", + "@babel/preset-typescript": "^7.22.5", + "@babel/runtime": "^7.22.6", + "@nrwl/js": "18.0.1", + "@nx/devkit": "18.0.1", + "@nx/workspace": "18.0.1", + "@phenomnomnominal/tsquery": "~5.0.1", + "babel-plugin-const-enum": "^1.0.1", + "babel-plugin-macros": "^2.8.0", + "babel-plugin-transform-typescript-metadata": "^0.3.1", + "chalk": "^4.1.0", + "columnify": "^1.6.0", + "detect-port": "^1.5.1", + "fast-glob": "3.2.7", + "fs-extra": "^11.1.0", + "ignore": "^5.0.4", + "js-tokens": "^4.0.0", + "minimatch": "9.0.3", + "npm-package-arg": "11.0.1", + "npm-run-path": "^4.0.1", + "ora": "5.3.0", + "semver": "^7.5.3", + "source-map-support": "0.5.19", + "ts-node": "10.9.1", + "tsconfig-paths": "^4.1.2", + "tslib": "^2.3.0" + }, + "peerDependencies": { + "verdaccio": "^5.0.4" + }, + "peerDependenciesMeta": { + "verdaccio": { + "optional": true + } + } + }, + "node_modules/@nx/node/node_modules/@phenomnomnominal/tsquery": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@phenomnomnominal/tsquery/-/tsquery-5.0.1.tgz", + "integrity": "sha512-3nVv+e2FQwsW8Aw6qTU6f+1rfcJ3hrcnvH/mu9i8YhxO+9sqbOfpL8m6PbET5+xKOlz/VSbp0RoYWYCtIsnmuA==", + "dev": true, + "dependencies": { + "esquery": "^1.4.0" + }, + "peerDependencies": { + "typescript": "^3 || ^4 || ^5" + } + }, + "node_modules/@nx/node/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@nx/node/node_modules/fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@nx/node/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@nx/node/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@nx/node/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@nx/node/node_modules/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "dependencies": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@nx/nx-darwin-arm64": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/nx-darwin-arm64/-/nx-darwin-arm64-18.0.1.tgz", + "integrity": "sha512-8/fRlTmOLrYyMCwIF/gEU/lYjA5pJ3hVDhmHpCy+VBvCHSJFF2JpFZMOV17XADirlqdlUDiuK1/ZueaTZAUcNg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nx/nx-darwin-x64": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/nx-darwin-x64/-/nx-darwin-x64-18.0.1.tgz", + "integrity": "sha512-ejOCxw2wmQimrxrgmsJD+RbblZCnstEfXzLCcqoIgxvuZJjNGvePQJ8INVFq8nFY/imGVFp2YcEiKX5HO6mvjg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nx/nx-freebsd-x64": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/nx-freebsd-x64/-/nx-freebsd-x64-18.0.1.tgz", + "integrity": "sha512-Hq64UBjbFN02dLCp12vzNLAx7U94p+NvuV3uh+G1Z9BpCD6FDIgoELY20J37wuz4H7B36vJgXPshWnGEw+XNdA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nx/nx-linux-arm-gnueabihf": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-18.0.1.tgz", + "integrity": "sha512-dPC2PTFuumglw8ZbkURRU+eksEAAyV5SyUcFnn1T9Il9+V1F1aZ9yQ0Yjg3YpMBHHkJO2hNgNjHTZp2hMTkW4A==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nx/nx-linux-arm64-gnu": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-18.0.1.tgz", + "integrity": "sha512-dX5ZF7OAFcE0CnWKOGWWD8yJ9Lz62EdSXafMwZgGWMlgTC1EBF5Rugg224LkCohl9J6Y9JPL6LGNzQsNEDu/oA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nx/nx-linux-arm64-musl": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-18.0.1.tgz", + "integrity": "sha512-86jV6SOUdWSvNnQ7QQVeNnSgANpZ+cTJQ4gWnVMkR6DKn2wmneMuChyWuK6I1qTbVMjH6qoclx4zHF51I6S6yQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nx/nx-linux-x64-gnu": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-18.0.1.tgz", + "integrity": "sha512-yvfifJYIJDgaiiWtrpGY7Ggd/wZC61zSunIMmzVyvckDa9NtjtsesYzh05oGy/FHht1avvOKEKrW7VTGOaVNpw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nx/nx-linux-x64-musl": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-18.0.1.tgz", + "integrity": "sha512-DZl0pJzla6y6Cr9SWpjLHeSkOefUmt4AC+ag1aqWWO3HSei7dtIBQ2fvM6C26Z8TMD75kXSpCd2nJmJSrb3gYw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nx/nx-win32-arm64-msvc": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-18.0.1.tgz", + "integrity": "sha512-fFUmqytVq/jUg6+f0QdnCTyjswNnDKSVeePvsLHU0us6ihmt7QgQS9x/xb41mXFSYLpUSU/ESZhpn7Ao3bdaAQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nx/nx-win32-x64-msvc": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-18.0.1.tgz", + "integrity": "sha512-vCzMpSfJNvAuK2LNW44XVV77GMI8anrVY9XQAFH5TmEYmsHs9NcyPYJL2QfjD/0oiGhtmepd9GfZJvr95SJ3xQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nx/react": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/react/-/react-18.0.1.tgz", + "integrity": "sha512-X6zZF0Nzgiz00LDzHt8paanna7tnwjxnRbOWVc0+TvJ7uOzFJkyThYcwQhK+69d+dQrW+Sot+lQmkrLCT+ilVg==", + "dev": true, + "dependencies": { + "@nrwl/react": "18.0.1", + "@nx/devkit": "18.0.1", + "@nx/eslint": "18.0.1", + "@nx/js": "18.0.1", + "@nx/web": "18.0.1", + "@phenomnomnominal/tsquery": "~5.0.1", + "@svgr/webpack": "^8.0.1", + "chalk": "^4.1.0", + "minimatch": "9.0.3", + "tslib": "^2.3.0" + } + }, + "node_modules/@nx/react/node_modules/@nrwl/js": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nrwl/js/-/js-18.0.1.tgz", + "integrity": "sha512-O1wfFdDMvObgqZgH/5qebm9YeN5cJ+UL2fJV2ZnsWVz3e8jhOQLflN3J7jXUWxzsz5stYiplsGpBjgXQRcNwaQ==", + "dev": true, + "dependencies": { + "@nx/js": "18.0.1" + } + }, + "node_modules/@nx/react/node_modules/@nrwl/web": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nrwl/web/-/web-18.0.1.tgz", + "integrity": "sha512-x6EXhFpgNsLHWQMVilkQBsXI2+otLbTT5TnpwfVUkulNXtA79M6rfnikzt+gQS/vp7l10dXb84Ys2yd4eUyaFQ==", + "dev": true, + "dependencies": { + "@nx/web": "18.0.1" + } + }, + "node_modules/@nx/react/node_modules/@nx/js": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/js/-/js-18.0.1.tgz", + "integrity": "sha512-U/l8K09UMrE4zVXEpmwpRWaY4fCrdcaaB3NXsEUwghJE6OuacADdaQ5ZWPxa0ji3nARKW7Umj87sOlUu8YjpTQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.23.2", + "@babel/plugin-proposal-decorators": "^7.22.7", + "@babel/plugin-transform-class-properties": "^7.22.5", + "@babel/plugin-transform-runtime": "^7.23.2", + "@babel/preset-env": "^7.23.2", + "@babel/preset-typescript": "^7.22.5", + "@babel/runtime": "^7.22.6", + "@nrwl/js": "18.0.1", + "@nx/devkit": "18.0.1", + "@nx/workspace": "18.0.1", + "@phenomnomnominal/tsquery": "~5.0.1", + "babel-plugin-const-enum": "^1.0.1", + "babel-plugin-macros": "^2.8.0", + "babel-plugin-transform-typescript-metadata": "^0.3.1", + "chalk": "^4.1.0", + "columnify": "^1.6.0", + "detect-port": "^1.5.1", + "fast-glob": "3.2.7", + "fs-extra": "^11.1.0", + "ignore": "^5.0.4", + "js-tokens": "^4.0.0", + "minimatch": "9.0.3", + "npm-package-arg": "11.0.1", + "npm-run-path": "^4.0.1", + "ora": "5.3.0", + "semver": "^7.5.3", + "source-map-support": "0.5.19", + "ts-node": "10.9.1", + "tsconfig-paths": "^4.1.2", + "tslib": "^2.3.0" + }, + "peerDependencies": { + "verdaccio": "^5.0.4" + }, + "peerDependenciesMeta": { + "verdaccio": { + "optional": true + } + } + }, + "node_modules/@nx/react/node_modules/@nx/web": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/web/-/web-18.0.1.tgz", + "integrity": "sha512-F/O/hhbk/tdqbLPfWg+fRVOUVzpmZr6m1tNiHMnK0FPY6r7i66JdhzA/KaaiuH2PD0VJ6gXvkYEauPRvvuOqjQ==", + "dev": true, + "dependencies": { + "@nrwl/web": "18.0.1", + "@nx/devkit": "18.0.1", + "@nx/js": "18.0.1", + "chalk": "^4.1.0", + "detect-port": "^1.5.1", + "http-server": "^14.1.0", + "tslib": "^2.3.0" + } + }, + "node_modules/@nx/react/node_modules/@phenomnomnominal/tsquery": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@phenomnomnominal/tsquery/-/tsquery-5.0.1.tgz", + "integrity": "sha512-3nVv+e2FQwsW8Aw6qTU6f+1rfcJ3hrcnvH/mu9i8YhxO+9sqbOfpL8m6PbET5+xKOlz/VSbp0RoYWYCtIsnmuA==", + "dev": true, + "dependencies": { + "esquery": "^1.4.0" + }, + "peerDependencies": { + "typescript": "^3 || ^4 || ^5" + } + }, + "node_modules/@nx/react/node_modules/@svgr/babel-plugin-add-jsx-attribute": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@nx/react/node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz", + "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@nx/react/node_modules/@svgr/babel-plugin-svg-dynamic-title": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz", + "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@nx/react/node_modules/@svgr/babel-plugin-svg-em-dimensions": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz", + "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@nx/react/node_modules/@svgr/babel-plugin-transform-react-native-svg": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz", + "integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@nx/react/node_modules/@svgr/babel-plugin-transform-svg-component": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz", + "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@nx/react/node_modules/@svgr/babel-preset": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz", + "integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==", + "dev": true, + "dependencies": { + "@svgr/babel-plugin-add-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0", + "@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0", + "@svgr/babel-plugin-svg-dynamic-title": "8.0.0", + "@svgr/babel-plugin-svg-em-dimensions": "8.0.0", + "@svgr/babel-plugin-transform-react-native-svg": "8.1.0", + "@svgr/babel-plugin-transform-svg-component": "8.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@nx/react/node_modules/@svgr/core": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", + "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "camelcase": "^6.2.0", + "cosmiconfig": "^8.1.3", + "snake-case": "^3.0.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@nx/react/node_modules/@svgr/hast-util-to-babel-ast": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz", + "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==", + "dev": true, + "dependencies": { + "@babel/types": "^7.21.3", + "entities": "^4.4.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@nx/react/node_modules/@svgr/plugin-jsx": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz", + "integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "@svgr/hast-util-to-babel-ast": "8.0.0", + "svg-parser": "^2.0.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@svgr/core": "*" + } + }, + "node_modules/@nx/react/node_modules/@svgr/plugin-svgo": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-8.1.0.tgz", + "integrity": "sha512-Ywtl837OGO9pTLIN/onoWLmDQ4zFUycI1g76vuKGEz6evR/ZTJlJuz3G/fIkb6OVBJ2g0o6CGJzaEjfmEo3AHA==", + "dev": true, + "dependencies": { + "cosmiconfig": "^8.1.3", + "deepmerge": "^4.3.1", + "svgo": "^3.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@svgr/core": "*" + } + }, + "node_modules/@nx/react/node_modules/@svgr/webpack": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-8.1.0.tgz", + "integrity": "sha512-LnhVjMWyMQV9ZmeEy26maJk+8HTIbd59cH4F2MJ439k9DqejRisfFNGAPvRYlKETuh9LrImlS8aKsBgKjMA8WA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.21.3", + "@babel/plugin-transform-react-constant-elements": "^7.21.3", + "@babel/preset-env": "^7.20.2", + "@babel/preset-react": "^7.18.6", + "@babel/preset-typescript": "^7.21.0", + "@svgr/core": "8.1.0", + "@svgr/plugin-jsx": "8.1.0", + "@svgr/plugin-svgo": "8.1.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@nx/react/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/@nx/react/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@nx/react/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/@nx/react/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nx/react/node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "dev": true, + "dependencies": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@nx/react/node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/@nx/react/node_modules/css-tree": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "dev": true, + "dependencies": { + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/@nx/react/node_modules/csso": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", + "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", + "dev": true, + "dependencies": { + "css-tree": "~2.2.0" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/@nx/react/node_modules/csso/node_modules/css-tree": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", + "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", + "dev": true, + "dependencies": { + "mdn-data": "2.0.28", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/@nx/react/node_modules/csso/node_modules/mdn-data": { + "version": "2.0.28", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", + "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", + "dev": true + }, + "node_modules/@nx/react/node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/@nx/react/node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/@nx/react/node_modules/domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "dev": true, + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/@nx/react/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/@nx/react/node_modules/fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@nx/react/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/@nx/react/node_modules/mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", + "dev": true + }, + "node_modules/@nx/react/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@nx/react/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@nx/react/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@nx/react/node_modules/svgo": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.2.0.tgz", + "integrity": "sha512-4PP6CMW/V7l/GmKRKzsLR8xxjdHTV4IMvhTnpuHwwBazSIlw5W/5SmPjN8Dwyt7lKbSJrRDgp4t9ph0HgChFBQ==", + "dev": true, + "dependencies": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^5.1.0", + "css-tree": "^2.3.1", + "css-what": "^6.1.0", + "csso": "^5.0.5", + "picocolors": "^1.0.0" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/svgo" + } + }, + "node_modules/@nx/react/node_modules/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "dependencies": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@nx/rollup": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/rollup/-/rollup-18.0.1.tgz", + "integrity": "sha512-VEAGyLV+1NLtPCgO03JwPuWl2kgGrcGA3/QiDMfvBkzXOXr2dY3rZd9qiGmDMeKXhQX6mI9QghIF8SWQWP/kSw==", + "dev": true, + "dependencies": { + "@nrwl/rollup": "18.0.1", + "@nx/devkit": "18.0.1", + "@nx/js": "18.0.1", + "@rollup/plugin-babel": "^5.3.0", + "@rollup/plugin-commonjs": "^20.0.0", + "@rollup/plugin-image": "^2.1.0", + "@rollup/plugin-json": "^4.1.0", + "@rollup/plugin-node-resolve": "^13.0.4", + "autoprefixer": "^10.4.9", + "babel-plugin-transform-async-to-promises": "^0.8.15", + "chalk": "^4.1.0", + "postcss": "^8.4.14", + "rollup": "^2.56.2", + "rollup-plugin-copy": "^3.4.0", + "rollup-plugin-peer-deps-external": "^2.2.4", + "rollup-plugin-postcss": "^4.0.1", + "rollup-plugin-typescript2": "0.34.1", + "rxjs": "^7.8.0", + "tslib": "^2.3.0" + } + }, + "node_modules/@nx/rollup/node_modules/@nrwl/js": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nrwl/js/-/js-18.0.1.tgz", + "integrity": "sha512-O1wfFdDMvObgqZgH/5qebm9YeN5cJ+UL2fJV2ZnsWVz3e8jhOQLflN3J7jXUWxzsz5stYiplsGpBjgXQRcNwaQ==", + "dev": true, + "dependencies": { + "@nx/js": "18.0.1" + } + }, + "node_modules/@nx/rollup/node_modules/@nx/js": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/js/-/js-18.0.1.tgz", + "integrity": "sha512-U/l8K09UMrE4zVXEpmwpRWaY4fCrdcaaB3NXsEUwghJE6OuacADdaQ5ZWPxa0ji3nARKW7Umj87sOlUu8YjpTQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.23.2", + "@babel/plugin-proposal-decorators": "^7.22.7", + "@babel/plugin-transform-class-properties": "^7.22.5", + "@babel/plugin-transform-runtime": "^7.23.2", + "@babel/preset-env": "^7.23.2", + "@babel/preset-typescript": "^7.22.5", + "@babel/runtime": "^7.22.6", + "@nrwl/js": "18.0.1", + "@nx/devkit": "18.0.1", + "@nx/workspace": "18.0.1", + "@phenomnomnominal/tsquery": "~5.0.1", + "babel-plugin-const-enum": "^1.0.1", + "babel-plugin-macros": "^2.8.0", + "babel-plugin-transform-typescript-metadata": "^0.3.1", + "chalk": "^4.1.0", + "columnify": "^1.6.0", + "detect-port": "^1.5.1", + "fast-glob": "3.2.7", + "fs-extra": "^11.1.0", + "ignore": "^5.0.4", + "js-tokens": "^4.0.0", + "minimatch": "9.0.3", + "npm-package-arg": "11.0.1", + "npm-run-path": "^4.0.1", + "ora": "5.3.0", + "semver": "^7.5.3", + "source-map-support": "0.5.19", + "ts-node": "10.9.1", + "tsconfig-paths": "^4.1.2", + "tslib": "^2.3.0" + }, + "peerDependencies": { + "verdaccio": "^5.0.4" + }, + "peerDependenciesMeta": { + "verdaccio": { + "optional": true + } + } + }, + "node_modules/@nx/rollup/node_modules/@phenomnomnominal/tsquery": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@phenomnomnominal/tsquery/-/tsquery-5.0.1.tgz", + "integrity": "sha512-3nVv+e2FQwsW8Aw6qTU6f+1rfcJ3hrcnvH/mu9i8YhxO+9sqbOfpL8m6PbET5+xKOlz/VSbp0RoYWYCtIsnmuA==", + "dev": true, + "dependencies": { + "esquery": "^1.4.0" + }, + "peerDependencies": { + "typescript": "^3 || ^4 || ^5" + } + }, + "node_modules/@nx/rollup/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@nx/rollup/node_modules/fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@nx/rollup/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@nx/rollup/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@nx/rollup/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@nx/rollup/node_modules/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "dependencies": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@nx/vite": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nx/vite/-/vite-18.3.4.tgz", + "integrity": "sha512-ZEynabx+GVjUF7t3I4VlY5ivXA2mOTQ4t+pjN9SNs9JdnLTMK/b/rErkNV7Bn2oQK56Ks74Tfbx9r7Dc5S4exg==", + "dev": true, + "dependencies": { + "@nrwl/vite": "18.3.4", + "@nx/devkit": "18.3.4", + "@nx/js": "18.3.4", + "@phenomnomnominal/tsquery": "~5.0.1", + "@swc/helpers": "~0.5.0", + "enquirer": "~2.3.6", + "tsconfig-paths": "^4.1.2" + }, + "peerDependencies": { + "vite": "^5.0.0", + "vitest": "^1.3.1" + } + }, + "node_modules/@nx/vite/node_modules/@nrwl/devkit": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nrwl/devkit/-/devkit-18.3.4.tgz", + "integrity": "sha512-Fty9Huqm12OYueU3uLJl3uvBUl5BvEyPfvw8+rLiNx9iftdEattM8C+268eAbIRRSLSOVXlWsJH4brlc6QZYYw==", + "dev": true, + "dependencies": { + "@nx/devkit": "18.3.4" + } + }, + "node_modules/@nx/vite/node_modules/@nx/devkit": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nx/devkit/-/devkit-18.3.4.tgz", + "integrity": "sha512-M3htxl5WvlNKK5KNOndCAApbyBCZNTFFs+rtdwvudNZk5+84zAAPaWzSoX9C4XLAW78/f98LzF68/ch05aN12A==", + "dev": true, + "dependencies": { + "@nrwl/devkit": "18.3.4", + "ejs": "^3.1.7", + "enquirer": "~2.3.6", + "ignore": "^5.0.4", + "semver": "^7.5.3", + "tmp": "~0.2.1", + "tslib": "^2.3.0", + "yargs-parser": "21.1.1" + }, + "peerDependencies": { + "nx": ">= 16 <= 19" + } + }, + "node_modules/@nx/vite/node_modules/@phenomnomnominal/tsquery": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@phenomnomnominal/tsquery/-/tsquery-5.0.1.tgz", + "integrity": "sha512-3nVv+e2FQwsW8Aw6qTU6f+1rfcJ3hrcnvH/mu9i8YhxO+9sqbOfpL8m6PbET5+xKOlz/VSbp0RoYWYCtIsnmuA==", + "dev": true, + "dependencies": { + "esquery": "^1.4.0" + }, + "peerDependencies": { + "typescript": "^3 || ^4 || ^5" + } + }, + "node_modules/@nx/vite/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@nx/vite/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@nx/vite/node_modules/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "dependencies": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@nx/web": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nx/web/-/web-18.3.4.tgz", + "integrity": "sha512-d+BCmJyELI2JXMtqnF7re8g/98pEyej8gUhL7ewywZ/LMEWJ8toG7yrEZoA6o2tlO3Fn63+7kUun7s5ZpkRElA==", + "dev": true, + "dependencies": { + "@nrwl/web": "18.3.4", + "@nx/devkit": "18.3.4", + "@nx/js": "18.3.4", + "chalk": "^4.1.0", + "detect-port": "^1.5.1", + "http-server": "^14.1.0", + "tslib": "^2.3.0" + } + }, + "node_modules/@nx/web/node_modules/@nrwl/devkit": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nrwl/devkit/-/devkit-18.3.4.tgz", + "integrity": "sha512-Fty9Huqm12OYueU3uLJl3uvBUl5BvEyPfvw8+rLiNx9iftdEattM8C+268eAbIRRSLSOVXlWsJH4brlc6QZYYw==", + "dev": true, + "dependencies": { + "@nx/devkit": "18.3.4" + } + }, + "node_modules/@nx/web/node_modules/@nx/devkit": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nx/devkit/-/devkit-18.3.4.tgz", + "integrity": "sha512-M3htxl5WvlNKK5KNOndCAApbyBCZNTFFs+rtdwvudNZk5+84zAAPaWzSoX9C4XLAW78/f98LzF68/ch05aN12A==", + "dev": true, + "dependencies": { + "@nrwl/devkit": "18.3.4", + "ejs": "^3.1.7", + "enquirer": "~2.3.6", + "ignore": "^5.0.4", + "semver": "^7.5.3", + "tmp": "~0.2.1", + "tslib": "^2.3.0", + "yargs-parser": "21.1.1" + }, + "peerDependencies": { + "nx": ">= 16 <= 19" + } + }, + "node_modules/@nx/web/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@nx/webpack": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/webpack/-/webpack-18.0.1.tgz", + "integrity": "sha512-W5/kO5LRGn4iWxZmXc3OeSCVnhCqD65lIMmsqZN3t25XLsaGSKjhUUWyf6G4zV29K0orkhZbqGmDyJqEnnOXbg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.23.2", + "@nrwl/webpack": "18.0.1", + "@nx/devkit": "18.0.1", + "@nx/js": "18.0.1", + "autoprefixer": "^10.4.9", + "babel-loader": "^9.1.2", + "browserslist": "^4.21.4", + "chalk": "^4.1.0", + "copy-webpack-plugin": "^10.2.4", + "css-loader": "^6.4.0", + "css-minimizer-webpack-plugin": "^5.0.0", + "fork-ts-checker-webpack-plugin": "7.2.13", + "less": "4.1.3", + "less-loader": "11.1.0", + "license-webpack-plugin": "^4.0.2", + "loader-utils": "^2.0.3", + "mini-css-extract-plugin": "~2.4.7", + "parse5": "4.0.0", + "postcss": "^8.4.14", + "postcss-import": "~14.1.0", + "postcss-loader": "^6.1.1", + "rxjs": "^7.8.0", + "sass": "^1.42.1", + "sass-loader": "^12.2.0", + "source-map-loader": "^3.0.0", + "style-loader": "^3.3.0", + "stylus": "^0.59.0", + "stylus-loader": "^7.1.0", + "terser-webpack-plugin": "^5.3.3", + "ts-loader": "^9.3.1", + "tsconfig-paths-webpack-plugin": "4.0.0", + "tslib": "^2.3.0", + "webpack": "^5.80.0", + "webpack-dev-server": "^4.9.3", + "webpack-node-externals": "^3.0.0", + "webpack-subresource-integrity": "^5.1.0" + } + }, + "node_modules/@nx/webpack/node_modules/@nrwl/js": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nrwl/js/-/js-18.0.1.tgz", + "integrity": "sha512-O1wfFdDMvObgqZgH/5qebm9YeN5cJ+UL2fJV2ZnsWVz3e8jhOQLflN3J7jXUWxzsz5stYiplsGpBjgXQRcNwaQ==", + "dev": true, + "dependencies": { + "@nx/js": "18.0.1" + } + }, + "node_modules/@nx/webpack/node_modules/@nx/js": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/js/-/js-18.0.1.tgz", + "integrity": "sha512-U/l8K09UMrE4zVXEpmwpRWaY4fCrdcaaB3NXsEUwghJE6OuacADdaQ5ZWPxa0ji3nARKW7Umj87sOlUu8YjpTQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.23.2", + "@babel/plugin-proposal-decorators": "^7.22.7", + "@babel/plugin-transform-class-properties": "^7.22.5", + "@babel/plugin-transform-runtime": "^7.23.2", + "@babel/preset-env": "^7.23.2", + "@babel/preset-typescript": "^7.22.5", + "@babel/runtime": "^7.22.6", + "@nrwl/js": "18.0.1", + "@nx/devkit": "18.0.1", + "@nx/workspace": "18.0.1", + "@phenomnomnominal/tsquery": "~5.0.1", + "babel-plugin-const-enum": "^1.0.1", + "babel-plugin-macros": "^2.8.0", + "babel-plugin-transform-typescript-metadata": "^0.3.1", + "chalk": "^4.1.0", + "columnify": "^1.6.0", + "detect-port": "^1.5.1", + "fast-glob": "3.2.7", + "fs-extra": "^11.1.0", + "ignore": "^5.0.4", + "js-tokens": "^4.0.0", + "minimatch": "9.0.3", + "npm-package-arg": "11.0.1", + "npm-run-path": "^4.0.1", + "ora": "5.3.0", + "semver": "^7.5.3", + "source-map-support": "0.5.19", + "ts-node": "10.9.1", + "tsconfig-paths": "^4.1.2", + "tslib": "^2.3.0" + }, + "peerDependencies": { + "verdaccio": "^5.0.4" + }, + "peerDependenciesMeta": { + "verdaccio": { + "optional": true + } + } + }, + "node_modules/@nx/webpack/node_modules/@phenomnomnominal/tsquery": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@phenomnomnominal/tsquery/-/tsquery-5.0.1.tgz", + "integrity": "sha512-3nVv+e2FQwsW8Aw6qTU6f+1rfcJ3hrcnvH/mu9i8YhxO+9sqbOfpL8m6PbET5+xKOlz/VSbp0RoYWYCtIsnmuA==", + "dev": true, + "dependencies": { + "esquery": "^1.4.0" + }, + "peerDependencies": { + "typescript": "^3 || ^4 || ^5" + } + }, + "node_modules/@nx/webpack/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@nx/webpack/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/@nx/webpack/node_modules/babel-loader": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.1.3.tgz", + "integrity": "sha512-xG3ST4DglodGf8qSwv0MdeWLhrDsw/32QMdTO5T1ZIp9gQur0HkCyFs7Awskr10JKXFXwpAhiCuYX5oGXnRGbw==", + "dev": true, + "dependencies": { + "find-cache-dir": "^4.0.0", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 14.15.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0", + "webpack": ">=5" + } + }, + "node_modules/@nx/webpack/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@nx/webpack/node_modules/find-cache-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-4.0.0.tgz", + "integrity": "sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==", + "dev": true, + "dependencies": { + "common-path-prefix": "^3.0.0", + "pkg-dir": "^7.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@nx/webpack/node_modules/find-up": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", + "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", + "dev": true, + "dependencies": { + "locate-path": "^7.1.0", + "path-exists": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@nx/webpack/node_modules/fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@nx/webpack/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/@nx/webpack/node_modules/locate-path": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", + "dev": true, + "dependencies": { + "p-locate": "^6.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@nx/webpack/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@nx/webpack/node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@nx/webpack/node_modules/p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "dev": true, + "dependencies": { + "p-limit": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@nx/webpack/node_modules/parse5": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", + "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", + "dev": true + }, + "node_modules/@nx/webpack/node_modules/path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/@nx/webpack/node_modules/pkg-dir": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-7.0.0.tgz", + "integrity": "sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==", + "dev": true, + "dependencies": { + "find-up": "^6.3.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@nx/webpack/node_modules/schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/@nx/webpack/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@nx/webpack/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@nx/webpack/node_modules/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "dependencies": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@nx/webpack/node_modules/yocto-queue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", + "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@nx/workspace": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/workspace/-/workspace-18.0.1.tgz", + "integrity": "sha512-yxURsmyx0XuXdxm/IoeM05CKdHepq2YGJjZISofMyqZG22Gr/TR2ygU2JxSgeuOdEkEHsEp11SSqFwb8CSVYZw==", + "dev": true, + "dependencies": { + "@nrwl/workspace": "18.0.1", + "@nx/devkit": "18.0.1", + "chalk": "^4.1.0", + "enquirer": "~2.3.6", + "nx": "18.0.1", + "tslib": "^2.3.0", + "yargs-parser": "21.1.1" + } + }, + "node_modules/@phenomnomnominal/tsquery": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@phenomnomnominal/tsquery/-/tsquery-4.1.1.tgz", + "integrity": "sha512-jjMmK1tnZbm1Jq5a7fBliM4gQwjxMU7TFoRNwIyzwlO+eHPRCFv/Nv+H/Gi1jc3WR7QURG8D5d0Tn12YGrUqBQ==", + "dev": true, + "dependencies": { + "esquery": "^1.0.1" + }, + "peerDependencies": { + "typescript": "^3 || ^4" + } + }, + "node_modules/@polka/url": { + "version": "1.0.0-next.25", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.25.tgz", + "integrity": "sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==" + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, + "node_modules/@rollup/plugin-babel": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", + "integrity": "sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.10.4", + "@rollup/pluginutils": "^3.1.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "@types/babel__core": "^7.1.9", + "rollup": "^1.20.0||^2.0.0" + }, + "peerDependenciesMeta": { + "@types/babel__core": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-commonjs": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-20.0.0.tgz", + "integrity": "sha512-5K0g5W2Ol8hAcTHqcTBHiA7M58tfmYi1o9KxeJuuRNpGaTa5iLjcyemBitCBcKXaHamOBBEH2dGom6v6Unmqjg==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^3.1.0", + "commondir": "^1.0.1", + "estree-walker": "^2.0.1", + "glob": "^7.1.6", + "is-reference": "^1.2.1", + "magic-string": "^0.25.7", + "resolve": "^1.17.0" + }, + "engines": { + "node": ">= 8.0.0" + }, + "peerDependencies": { + "rollup": "^2.38.3" + } + }, + "node_modules/@rollup/plugin-image": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-image/-/plugin-image-2.1.1.tgz", + "integrity": "sha512-AgP4U85zuQJdUopLUCM+hTf45RepgXeTb8EJsleExVy99dIoYpt3ZlDYJdKmAc2KLkNntCDg6BPJvgJU3uGF+g==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^3.1.0", + "mini-svg-data-uri": "^1.2.3" + }, + "engines": { + "node": ">= 8.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0 || ^2.0.0" + } + }, + "node_modules/@rollup/plugin-json": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-4.1.0.tgz", + "integrity": "sha512-yfLbTdNS6amI/2OpmbiBoW12vngr5NW2jCJVZSBEz+H5KfUJZ2M7sDjk0U6GOOdCWFVScShte29o9NezJ53TPw==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^3.0.8" + }, + "peerDependencies": { + "rollup": "^1.20.0 || ^2.0.0" + } + }, + "node_modules/@rollup/plugin-node-resolve": { + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.3.0.tgz", + "integrity": "sha512-Lus8rbUo1eEcnS4yTFKLZrVumLPY+YayBdWXgFSHYhTT2iJbMhoaaBL3xl5NCdeRytErGr8tZ0L71BMRmnlwSw==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^3.1.0", + "@types/resolve": "1.17.1", + "deepmerge": "^4.2.2", + "is-builtin-module": "^3.1.0", + "is-module": "^1.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "peerDependencies": { + "rollup": "^2.42.0" + } + }, + "node_modules/@rollup/pluginutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", + "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", + "dev": true, + "dependencies": { + "@types/estree": "0.0.39", + "estree-walker": "^1.0.1", + "picomatch": "^2.2.2" + }, + "engines": { + "node": ">= 8.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0" + } + }, + "node_modules/@rollup/pluginutils/node_modules/@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", + "dev": true + }, + "node_modules/@rollup/pluginutils/node_modules/estree-walker": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", + "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", + "dev": true + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.17.2.tgz", + "integrity": "sha512-NM0jFxY8bB8QLkoKxIQeObCaDlJKewVlIEkuyYKm5An1tdVZ966w2+MPQ2l8LBZLjR+SgyV+nRkTIunzOYBMLQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.17.2.tgz", + "integrity": "sha512-yeX/Usk7daNIVwkq2uGoq2BYJKZY1JfyLTaHO/jaiSwi/lsf8fTFoQW/n6IdAsx5tx+iotu2zCJwz8MxI6D/Bw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.17.2.tgz", + "integrity": "sha512-kcMLpE6uCwls023+kknm71ug7MZOrtXo+y5p/tsg6jltpDtgQY1Eq5sGfHcQfb+lfuKwhBmEURDga9N0ol4YPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.17.2.tgz", + "integrity": "sha512-AtKwD0VEx0zWkL0ZjixEkp5tbNLzX+FCqGG1SvOu993HnSz4qDI6S4kGzubrEJAljpVkhRSlg5bzpV//E6ysTQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.17.2.tgz", + "integrity": "sha512-3reX2fUHqN7sffBNqmEyMQVj/CKhIHZd4y631duy0hZqI8Qoqf6lTtmAKvJFYa6bhU95B1D0WgzHkmTg33In0A==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.17.2.tgz", + "integrity": "sha512-uSqpsp91mheRgw96xtyAGP9FW5ChctTFEoXP0r5FAzj/3ZRv3Uxjtc7taRQSaQM/q85KEKjKsZuiZM3GyUivRg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.17.2.tgz", + "integrity": "sha512-EMMPHkiCRtE8Wdk3Qhtciq6BndLtstqZIroHiiGzB3C5LDJmIZcSzVtLRbwuXuUft1Cnv+9fxuDtDxz3k3EW2A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.17.2.tgz", + "integrity": "sha512-NMPylUUZ1i0z/xJUIx6VUhISZDRT+uTWpBcjdv0/zkp7b/bQDF+NfnfdzuTiB1G6HTodgoFa93hp0O1xl+/UbA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.17.2.tgz", + "integrity": "sha512-T19My13y8uYXPw/L/k0JYaX1fJKFT/PWdXiHr8mTbXWxjVF1t+8Xl31DgBBvEKclw+1b00Chg0hxE2O7bTG7GQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.17.2.tgz", + "integrity": "sha512-BOaNfthf3X3fOWAB+IJ9kxTgPmMqPPH5f5k2DcCsRrBIbWnaJCgX2ll77dV1TdSy9SaXTR5iDXRL8n7AnoP5cg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.17.2.tgz", + "integrity": "sha512-W0UP/x7bnn3xN2eYMql2T/+wpASLE5SjObXILTMPUBDB/Fg/FxC+gX4nvCfPBCbNhz51C+HcqQp2qQ4u25ok6g==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.17.2.tgz", + "integrity": "sha512-Hy7pLwByUOuyaFC6mAr7m+oMC+V7qyifzs/nW2OJfC8H4hbCzOX07Ov0VFk/zP3kBsELWNFi7rJtgbKYsav9QQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.17.2.tgz", + "integrity": "sha512-h1+yTWeYbRdAyJ/jMiVw0l6fOOm/0D1vNLui9iPuqgRGnXA0u21gAqOyB5iHjlM9MMfNOm9RHCQ7zLIzT0x11Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.17.2.tgz", + "integrity": "sha512-tmdtXMfKAjy5+IQsVtDiCfqbynAQE/TQRpWdVataHmhMb9DCoJxp9vLcCBjEQWMiUYxO1QprH/HbY9ragCEFLA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.17.2.tgz", + "integrity": "sha512-7II/QCSTAHuE5vdZaQEwJq2ZACkBpQDOmQsE6D6XUbnBHW8IAhm4eTufL6msLJorzrHDFv3CF8oCA/hSIRuZeQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.17.2.tgz", + "integrity": "sha512-TGGO7v7qOq4CYmSBVEYpI1Y5xDuCEnbVC5Vth8mOsW0gDSzxNrVERPc790IGHsrT2dQSimgMr9Ub3Y1Jci5/8w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rushstack/eslint-patch": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz", + "integrity": "sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==", + "dev": true + }, + "node_modules/@sideway/address": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz", + "integrity": "sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@sideway/formula": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==" + }, + "node_modules/@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==" + }, + "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": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@slorber/static-site-generator-webpack-plugin": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@slorber/static-site-generator-webpack-plugin/-/static-site-generator-webpack-plugin-4.0.7.tgz", + "integrity": "sha512-Ug7x6z5lwrz0WqdnNFOMYrDQNTPAprvHLSh6+/fmml3qUiz6l5eq+2MzLKWtn/q5K5NpSiFsZTP/fck/3vjSxA==", + "dependencies": { + "eval": "^0.1.8", + "p-map": "^4.0.0", + "webpack-sources": "^3.2.2" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@svgr/babel-plugin-add-jsx-attribute": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-6.5.1.tgz", + "integrity": "sha512-9PYGcXrAxitycIjRmZB+Q0JaN07GZIWaTBIGQzfaZv+qr1n8X1XUEJ5rZ/vx6OVD9RRYlrNnXWExQXcmZeD/BQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-attribute": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz", + "integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-6.5.1.tgz", + "integrity": "sha512-8DPaVVE3fd5JKuIC29dqyMB54sA6mfgki2H2+swh+zNJoynC8pMPzOkidqHOSc6Wj032fhl8Z0TVn1GiPpAiJg==", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-svg-dynamic-title": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-6.5.1.tgz", + "integrity": "sha512-FwOEi0Il72iAzlkaHrlemVurgSQRDFbk0OC8dSvD5fSBPHltNh7JtLsxmZUhjYBZo2PpcU/RJvvi6Q0l7O7ogw==", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-svg-em-dimensions": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-6.5.1.tgz", + "integrity": "sha512-gWGsiwjb4tw+ITOJ86ndY/DZZ6cuXMNE/SjcDRg+HLuCmwpcjOktwRF9WgAiycTqJD/QXqL2f8IzE2Rzh7aVXA==", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-transform-react-native-svg": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-6.5.1.tgz", + "integrity": "sha512-2jT3nTayyYP7kI6aGutkyfJ7UMGtuguD72OjeGLwVNyfPRBD8zQthlvL+fAbAKk5n9ZNcvFkp/b1lZ7VsYqVJg==", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-transform-svg-component": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-6.5.1.tgz", + "integrity": "sha512-a1p6LF5Jt33O3rZoVRBqdxL350oge54iZWHNI6LJB5tQ7EelvD/Mb1mfBiZNAan0dt4i3VArkFRjA4iObuNykQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-preset": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-6.5.1.tgz", + "integrity": "sha512-6127fvO/FF2oi5EzSQOAjo1LE3OtNVh11R+/8FXa+mHx1ptAaS4cknIjnUA7e6j6fwGGJ17NzaTJFUwOV2zwCw==", + "dependencies": { + "@svgr/babel-plugin-add-jsx-attribute": "^6.5.1", + "@svgr/babel-plugin-remove-jsx-attribute": "*", + "@svgr/babel-plugin-remove-jsx-empty-expression": "*", + "@svgr/babel-plugin-replace-jsx-attribute-value": "^6.5.1", + "@svgr/babel-plugin-svg-dynamic-title": "^6.5.1", + "@svgr/babel-plugin-svg-em-dimensions": "^6.5.1", + "@svgr/babel-plugin-transform-react-native-svg": "^6.5.1", + "@svgr/babel-plugin-transform-svg-component": "^6.5.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/core": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-6.5.1.tgz", + "integrity": "sha512-/xdLSWxK5QkqG524ONSjvg3V/FkNyCv538OIBdQqPNaAta3AsXj/Bd2FbvR87yMbXO2hFSWiAe/Q6IkVPDw+mw==", + "dependencies": { + "@babel/core": "^7.19.6", + "@svgr/babel-preset": "^6.5.1", + "@svgr/plugin-jsx": "^6.5.1", + "camelcase": "^6.2.0", + "cosmiconfig": "^7.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/core/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@svgr/hast-util-to-babel-ast": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-6.5.1.tgz", + "integrity": "sha512-1hnUxxjd83EAxbL4a0JDJoD3Dao3hmjvyvyEV8PzWmLK3B9m9NPlW7GKjFyoWE8nM7HnXzPcmmSyOW8yOddSXw==", + "dependencies": { + "@babel/types": "^7.20.0", + "entities": "^4.4.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/hast-util-to-babel-ast/node_modules/entities": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", + "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/@svgr/plugin-jsx": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-6.5.1.tgz", + "integrity": "sha512-+UdQxI3jgtSjCykNSlEMuy1jSRQlGC7pqBCPvkG/2dATdWo082zHTTK3uhnAju2/6XpE6B5mZ3z4Z8Ns01S8Gw==", + "dependencies": { + "@babel/core": "^7.19.6", + "@svgr/babel-preset": "^6.5.1", + "@svgr/hast-util-to-babel-ast": "^6.5.1", + "svg-parser": "^2.0.4" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@svgr/core": "^6.0.0" + } + }, + "node_modules/@svgr/plugin-svgo": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-6.5.1.tgz", + "integrity": "sha512-omvZKf8ixP9z6GWgwbtmP9qQMPX4ODXi+wzbVZgomNFsUIlHA1sf4fThdwTWSsZGgvGAG6yE+b/F5gWUkcZ/iQ==", + "dependencies": { + "cosmiconfig": "^7.0.1", + "deepmerge": "^4.2.2", + "svgo": "^2.8.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@svgr/core": "*" + } + }, + "node_modules/@svgr/webpack": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-6.5.1.tgz", + "integrity": "sha512-cQ/AsnBkXPkEK8cLbv4Dm7JGXq2XrumKnL1dRpJD9rIO2fTIlJI9a1uCciYG1F2aUsox/hJQyNGbt3soDxSRkA==", + "dependencies": { + "@babel/core": "^7.19.6", + "@babel/plugin-transform-react-constant-elements": "^7.18.12", + "@babel/preset-env": "^7.19.4", + "@babel/preset-react": "^7.18.6", + "@babel/preset-typescript": "^7.18.6", + "@svgr/core": "^6.5.1", + "@svgr/plugin-jsx": "^6.5.1", + "@svgr/plugin-svgo": "^6.5.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@swc-node/core": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/@swc-node/core/-/core-1.13.1.tgz", + "integrity": "sha512-emB5l2nZsXjUEAuusqjYvWnQMLWZp6K039Mv8aq5SX1rsNM/N7DNhw1i4/DX7AyzNZ0tT+ASWyTvqEURldp5HA==", + "dev": true, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@swc/core": ">= 1.4.13", + "@swc/types": ">= 0.1" + } + }, + "node_modules/@swc-node/register": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@swc-node/register/-/register-1.8.0.tgz", + "integrity": "sha512-8K3589HoBSmVmrEVrtr4K5sWEithpGDzcFGic81OW0A9sZY38IV5EGRODQWCk0SBDyLhaF+pid120vJAtsHo1A==", + "dev": true, + "dependencies": { + "@swc-node/core": "^1.12.0", + "@swc-node/sourcemap-support": "^0.4.0", + "colorette": "^2.0.20", + "debug": "^4.3.4", + "pirates": "^4.0.6", + "tslib": "^2.6.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@swc/core": ">= 1.3", + "typescript": ">= 4.3" + } + }, + "node_modules/@swc-node/sourcemap-support": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@swc-node/sourcemap-support/-/sourcemap-support-0.4.0.tgz", + "integrity": "sha512-weuRmYTO+4yOtHtPZHXlPdA1dJJJp3QOoZAFZ6uZidu992F2X5v1fQdnb26xs1o3Ex/e2sYhRyY5R6NGNuoATQ==", + "dev": true, + "dependencies": { + "source-map-support": "^0.5.21", + "tslib": "^2.6.2" + } + }, + "node_modules/@swc-node/sourcemap-support/node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/@swc/core": { + "version": "1.3.107", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.107.tgz", + "integrity": "sha512-zKhqDyFcTsyLIYK1iEmavljZnf4CCor5pF52UzLAz4B6Nu/4GLU+2LQVAf+oRHjusG39PTPjd2AlRT3f3QWfsQ==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@swc/counter": "^0.1.1", + "@swc/types": "^0.1.5" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.3.107", + "@swc/core-darwin-x64": "1.3.107", + "@swc/core-linux-arm-gnueabihf": "1.3.107", + "@swc/core-linux-arm64-gnu": "1.3.107", + "@swc/core-linux-arm64-musl": "1.3.107", + "@swc/core-linux-x64-gnu": "1.3.107", + "@swc/core-linux-x64-musl": "1.3.107", + "@swc/core-win32-arm64-msvc": "1.3.107", + "@swc/core-win32-ia32-msvc": "1.3.107", + "@swc/core-win32-x64-msvc": "1.3.107" + }, + "peerDependencies": { + "@swc/helpers": "^0.5.0" + }, + "peerDependenciesMeta": { + "@swc/helpers": { + "optional": true + } + } + }, + "node_modules/@swc/core-darwin-arm64": { + "version": "1.3.107", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.107.tgz", + "integrity": "sha512-47tD/5vSXWxPd0j/ZllyQUg4bqalbQTsmqSw0J4dDdS82MWqCAwUErUrAZPRjBkjNQ6Kmrf5rpCWaGTtPw+ngw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-darwin-x64": { + "version": "1.3.107", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.107.tgz", + "integrity": "sha512-hwiLJ2ulNkBGAh1m1eTfeY1417OAYbRGcb/iGsJ+LuVLvKAhU/itzsl535CvcwAlt2LayeCFfcI8gdeOLeZa9A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.3.107", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.107.tgz", + "integrity": "sha512-I2wzcC0KXqh0OwymCmYwNRgZ9nxX7DWnOOStJXV3pS0uB83TXAkmqd7wvMBuIl9qu4Hfomi9aDM7IlEEn9tumQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.3.107", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.107.tgz", + "integrity": "sha512-HWgnn7JORYlOYnGsdunpSF8A+BCZKPLzLtEUA27/M/ZuANcMZabKL9Zurt7XQXq888uJFAt98Gy+59PU90aHKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-musl": { + "version": "1.3.107", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.107.tgz", + "integrity": "sha512-vfPF74cWfAm8hyhS8yvYI94ucMHIo8xIYU+oFOW9uvDlGQRgnUf/6DEVbLyt/3yfX5723Ln57U8uiMALbX5Pyw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-gnu": { + "version": "1.3.107", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.107.tgz", + "integrity": "sha512-uBVNhIg0ip8rH9OnOsCARUFZ3Mq3tbPHxtmWk9uAa5u8jQwGWeBx5+nTHpDOVd3YxKb6+5xDEI/edeeLpha/9g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-musl": { + "version": "1.3.107", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.107.tgz", + "integrity": "sha512-mvACkUvzSIB12q1H5JtabWATbk3AG+pQgXEN95AmEX2ZA5gbP9+B+mijsg7Sd/3tboHr7ZHLz/q3SHTvdFJrEw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.3.107", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.107.tgz", + "integrity": "sha512-J3P14Ngy/1qtapzbguEH41kY109t6DFxfbK4Ntz9dOWNuVY3o9/RTB841ctnJk0ZHEG+BjfCJjsD2n8H5HcaOA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.3.107", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.107.tgz", + "integrity": "sha512-ZBUtgyjTHlz8TPJh7kfwwwFma+ktr6OccB1oXC8fMSopD0AxVnQasgun3l3099wIsAB9eEsJDQ/3lDkOLs1gBA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-x64-msvc": { + "version": "1.3.107", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.107.tgz", + "integrity": "sha512-Eyzo2XRqWOxqhE1gk9h7LWmUf4Bp4Xn2Ttb0ayAXFp6YSTxQIThXcT9kipXZqcpxcmDwoq8iWbbf2P8XL743EA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "dev": true + }, + "node_modules/@swc/helpers": { + "version": "0.5.11", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.11.tgz", + "integrity": "sha512-YNlnKRWF2sVojTpIyzwou9XoTNbzbzONwRhOoniEioF1AtaitTvVZblaQRrAzChWQ1bLYyYSWzM18y4WwgzJ+A==", + "dev": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@swc/types": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.6.tgz", + "integrity": "sha512-/JLo/l2JsT/LRd80C3HfbmVpxOAJ11FO2RCEslFrgzLltoP9j8XIbsyDcfCt2WWyX+CM96rBoNM+IToAkFOugg==", + "dev": true, + "dependencies": { + "@swc/counter": "^0.1.3" + } + }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "dev": true + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.5.tgz", + "integrity": "sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/bonjour": { + "version": "3.5.10", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", + "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/cacheable-request": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" + } + }, + "node_modules/@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect-history-api-fallback": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz", + "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==", + "dependencies": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, + "node_modules/@types/eslint": { + "version": "8.4.10", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.10.tgz", + "integrity": "sha512-Sl/HOqN8NKPmhWo2VBEPm0nvHnu2LL3v9vKo8MEq0EtbJ4eVzGPl41VNPvn5E1i5poMk4/XD8UriLHpJvEP/Nw==", + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", + "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" + }, + "node_modules/@types/express": { + "version": "4.17.14", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.14.tgz", + "integrity": "sha512-TEbt+vaPFQ+xpxFLFssxUDXj5cWCxZJjIcB7Yg0k0GMHGtgtQgpvx/MUQUeAkNbA9AAGrwkAsoeItdTgS7FMyg==", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.17.31", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.31.tgz", + "integrity": "sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q==", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "node_modules/@types/follow-redirects": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@types/follow-redirects/-/follow-redirects-1.14.1.tgz", + "integrity": "sha512-THBEFwqsLuU/K62B5JRwab9NW97cFmL4Iy34NTMX0bMycQVzq2q7PKOkhfivIwxdpa/J72RppgC42vCHfwKJ0Q==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/fs-extra": { + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.5.tgz", + "integrity": "sha512-0dzKcwO+S8s2kuF5Z9oUWatQJj5Uq/iqphEtE3GQJVRRYm/tD1LglU2UnXi2A8jLq5umkGouOXOR9y0n613ZwQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", + "dev": true, + "dependencies": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/hast": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.4.tgz", + "integrity": "sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/history": { + "version": "4.7.11", + "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.11.tgz", + "integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==" + }, + "node_modules/@types/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==" + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", + "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==" + }, + "node_modules/@types/http-proxy": { + "version": "1.17.9", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.9.tgz", + "integrity": "sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jsdom": { + "version": "20.0.1", + "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz", + "integrity": "sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/tough-cookie": "*", + "parse5": "^7.0.0" + } + }, + "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==" + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, + "node_modules/@types/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/linkify-it": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.2.tgz", + "integrity": "sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==" + }, + "node_modules/@types/markdown-it": { + "version": "12.2.3", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", + "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", + "dependencies": { + "@types/linkify-it": "*", + "@types/mdurl": "*" + } + }, + "node_modules/@types/mdast": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.11.tgz", + "integrity": "sha512-Y/uImid8aAwrEA24/1tcRZwpxX3pIFTSilcNDKSPn+Y2iDywSEachzRuvgAYYLR3wpGXAsMbv5lvKLDZLeYPAw==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/mdurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.2.tgz", + "integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==" + }, + "node_modules/@types/mime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", + "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==" + }, + "node_modules/@types/mime-types": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.1.tgz", + "integrity": "sha512-vXOTGVSLR2jMw440moWTC7H19iUyLtP3Z1YTj7cSsubOICinjMxFeb/V57v9QdyyPGbbWolUFSSmSiRSn94tFw==", + "dev": true + }, + "node_modules/@types/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", + "dev": true + }, + "node_modules/@types/node": { + "version": "18.19.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.11.tgz", + "integrity": "sha512-hzdHPKpDdp5bEcRq1XTlZ2ntVjLcHCTV73dEcGg02eSY/+9AZ+jlfz6i00+zOrunMWenjHuI49J8J7Y9uz50JQ==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true + }, + "node_modules/@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" + }, + "node_modules/@types/pg": { + "version": "8.6.5", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.6.5.tgz", + "integrity": "sha512-tOkGtAqRVkHa/PVZicq67zuujI4Oorfglsr2IbKofDwBSysnaqSx7W1mDqFqdkGE6Fbgh+PZAl0r/BWON/mozw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "pg-protocol": "*", + "pg-types": "^2.2.0" + } + }, + "node_modules/@types/prop-types": { + "version": "15.7.5", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" + }, + "node_modules/@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" + }, + "node_modules/@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" + }, + "node_modules/@types/react": { + "version": "18.2.24", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.24.tgz", + "integrity": "sha512-Ee0Jt4sbJxMu1iDcetZEIKQr99J1Zfb6D4F3qfUWoR1JpInkY1Wdg4WwCyBjL257D0+jGqSl1twBjV8iCaC0Aw==", + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.2.9", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.9.tgz", + "integrity": "sha512-6nNhVzZ9joQ6F7lozrASuQKC0Kf6ArYMU+DqA2ZrUbB+d+9lC6ZLn1GxiEBI1edmAwvTULtuJ6uPZpv3XudwUg==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/react-router": { + "version": "5.1.20", + "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.20.tgz", + "integrity": "sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==", + "dependencies": { + "@types/history": "^4.7.11", + "@types/react": "*" + } + }, + "node_modules/@types/react-router-config": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/@types/react-router-config/-/react-router-config-5.0.7.tgz", + "integrity": "sha512-pFFVXUIydHlcJP6wJm7sDii5mD/bCmmAY0wQzq+M+uX7bqS95AQqHZWP1iNMKrWVQSuHIzj5qi9BvrtLX2/T4w==", + "dependencies": { + "@types/history": "^4.7.11", + "@types/react": "*", + "@types/react-router": "^5.1.0" + } + }, + "node_modules/@types/react-router-dom": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.3.3.tgz", + "integrity": "sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==", + "dependencies": { + "@types/history": "^4.7.11", + "@types/react": "*", + "@types/react-router": "*" + } + }, + "node_modules/@types/resolve": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", + "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/responselike": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", + "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" + }, + "node_modules/@types/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-pSAff4IAxJjfAXUG6tFkO7dsSbTmf8CtUpfhhZ5VhkRpC4628tJhh3+V6H1E+/Gs9piSzYKT5yzHO5M4GG9jkw==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/scheduler": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" + }, + "node_modules/@types/semver": { + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", + "dev": true + }, + "node_modules/@types/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==", + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==", + "dependencies": { + "@types/mime": "*", + "@types/node": "*" + } + }, + "node_modules/@types/sockjs": { + "version": "0.3.33", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", + "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true + }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "dev": true + }, + "node_modules/@types/unist": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", + "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==" + }, + "node_modules/@types/vscode": { + "version": "1.73.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.73.0.tgz", + "integrity": "sha512-FhkfF7V3fj7S3WqXu7AxFesBLO3uMkdCPJJPbwyZXezv2xJ6xBWHYM2CmkkbO8wT9Fr3KipwxGGOoQRrYq7mHg==", + "dev": true + }, + "node_modules/@types/ws": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", + "integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/yargs": { + "version": "17.0.32", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", + "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.20.0.tgz", + "integrity": "sha512-fTwGQUnjhoYHeSF6m5pWNkzmDDdsKELYrOBxhjMrofPqCkoC2k3B2wvGHFxa1CTIqkEn88nlW1HVMztjo2K8Hg==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.20.0", + "@typescript-eslint/type-utils": "6.20.0", + "@typescript-eslint/utils": "6.20.0", + "@typescript-eslint/visitor-keys": "6.20.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.20.0.tgz", + "integrity": "sha512-p4rvHQRDTI1tGGMDFQm+GtxP1ZHyAh64WANVoyEcNMpaTFn3ox/3CcgtIlELnRfKzSs/DwYlDccJEtr3O6qBvA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.20.0", + "@typescript-eslint/visitor-keys": "6.20.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.20.0.tgz", + "integrity": "sha512-MM9mfZMAhiN4cOEcUOEx+0HmuaW3WBfukBZPCfwSqFnQy0grXYtngKCqpQN339X3RrwtzspWJrpbrupKYUSBXQ==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.20.0.tgz", + "integrity": "sha512-RnRya9q5m6YYSpBN7IzKu9FmLcYtErkDkc8/dKv81I9QiLLtVBHrjz+Ev/crAqgMNW2FCsoZF4g2QUylMnJz+g==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.20.0", + "@typescript-eslint/visitor-keys": "6.20.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.20.0.tgz", + "integrity": "sha512-/EKuw+kRu2vAqCoDwDCBtDRU6CTKbUmwwI7SH7AashZ+W+7o8eiyy6V2cdOqN49KsTcASWsC5QeghYuRDTyOOg==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.20.0", + "@typescript-eslint/types": "6.20.0", + "@typescript-eslint/typescript-estree": "6.20.0", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.20.0.tgz", + "integrity": "sha512-E8Cp98kRe4gKHjJD4NExXKz/zOJ1A2hhZc+IMVD6i7w4yjIvh6VyuRI0gRtxAsXtoC35uGMaQ9rjI2zJaXDEAw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.20.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/experimental-utils": { + "version": "5.46.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.46.1.tgz", + "integrity": "sha512-M79mkB+wOuiBG8jzOVNA2h5izOip5CNPZV1K3tvE/qry/1Oh/bnKYhNWQNiH2h9O3B73YK60GmiqrUpprnQ5sQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/utils": "5.46.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/experimental-utils/node_modules/@typescript-eslint/scope-manager": { + "version": "5.46.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.46.1.tgz", + "integrity": "sha512-iOChVivo4jpwUdrJZyXSMrEIM/PvsbbDOX1y3UCKjSgWn+W89skxWaYXACQfxmIGhPVpRWK/VWPYc+bad6smIA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.46.1", + "@typescript-eslint/visitor-keys": "5.46.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/experimental-utils/node_modules/@typescript-eslint/types": { + "version": "5.46.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.46.1.tgz", + "integrity": "sha512-Z5pvlCaZgU+93ryiYUwGwLl9AQVB/PQ1TsJ9NZ/gHzZjN7g9IAn6RSDkpCV8hqTwAiaj6fmCcKSQeBPlIpW28w==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/experimental-utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "5.46.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.46.1.tgz", + "integrity": "sha512-j9W4t67QiNp90kh5Nbr1w92wzt+toiIsaVPnEblB2Ih2U9fqBTyqV9T3pYWZBRt6QoMh/zVWP59EpuCjc4VRBg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.46.1", + "@typescript-eslint/visitor-keys": "5.46.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/experimental-utils/node_modules/@typescript-eslint/utils": { + "version": "5.46.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.46.1.tgz", + "integrity": "sha512-RBdBAGv3oEpFojaCYT4Ghn4775pdjvwfDOfQ2P6qzNVgQOVrnSPe5/Pb88kv7xzYQjoio0eKHKB9GJ16ieSxvA==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.46.1", + "@typescript-eslint/types": "5.46.1", + "@typescript-eslint/typescript-estree": "5.46.1", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/experimental-utils/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.46.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.46.1.tgz", + "integrity": "sha512-jczZ9noovXwy59KjRTk1OftT78pwygdcmCuBf8yMoWt/8O8l+6x2LSEze0E4TeepXK4MezW3zGSyoDRZK7Y9cg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.46.1", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/experimental-utils/node_modules/fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/@typescript-eslint/experimental-utils/node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@typescript-eslint/experimental-utils/node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.20.0.tgz", + "integrity": "sha512-bYerPDF/H5v6V76MdMYhjwmwgMA+jlPVqjSDq2cRqMi8bP5sR3Z+RLOiOMad3nsnmDVmn2gAFCyNgh/dIrfP/w==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "6.20.0", + "@typescript-eslint/types": "6.20.0", + "@typescript-eslint/typescript-estree": "6.20.0", + "@typescript-eslint/visitor-keys": "6.20.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.20.0.tgz", + "integrity": "sha512-p4rvHQRDTI1tGGMDFQm+GtxP1ZHyAh64WANVoyEcNMpaTFn3ox/3CcgtIlELnRfKzSs/DwYlDccJEtr3O6qBvA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.20.0", + "@typescript-eslint/visitor-keys": "6.20.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.20.0.tgz", + "integrity": "sha512-MM9mfZMAhiN4cOEcUOEx+0HmuaW3WBfukBZPCfwSqFnQy0grXYtngKCqpQN339X3RrwtzspWJrpbrupKYUSBXQ==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.20.0.tgz", + "integrity": "sha512-RnRya9q5m6YYSpBN7IzKu9FmLcYtErkDkc8/dKv81I9QiLLtVBHrjz+Ev/crAqgMNW2FCsoZF4g2QUylMnJz+g==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.20.0", + "@typescript-eslint/visitor-keys": "6.20.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.20.0.tgz", + "integrity": "sha512-E8Cp98kRe4gKHjJD4NExXKz/zOJ1A2hhZc+IMVD6i7w4yjIvh6VyuRI0gRtxAsXtoC35uGMaQ9rjI2zJaXDEAw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.20.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.20.0.tgz", + "integrity": "sha512-qnSobiJQb1F5JjN0YDRPHruQTrX7ICsmltXhkV536mp4idGAYrIyr47zF/JmkJtEcAVnIz4gUYJ7gOZa6SmN4g==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "6.20.0", + "@typescript-eslint/utils": "6.20.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/scope-manager": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.20.0.tgz", + "integrity": "sha512-p4rvHQRDTI1tGGMDFQm+GtxP1ZHyAh64WANVoyEcNMpaTFn3ox/3CcgtIlELnRfKzSs/DwYlDccJEtr3O6qBvA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.20.0", + "@typescript-eslint/visitor-keys": "6.20.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.20.0.tgz", + "integrity": "sha512-MM9mfZMAhiN4cOEcUOEx+0HmuaW3WBfukBZPCfwSqFnQy0grXYtngKCqpQN339X3RrwtzspWJrpbrupKYUSBXQ==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.20.0.tgz", + "integrity": "sha512-RnRya9q5m6YYSpBN7IzKu9FmLcYtErkDkc8/dKv81I9QiLLtVBHrjz+Ev/crAqgMNW2FCsoZF4g2QUylMnJz+g==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.20.0", + "@typescript-eslint/visitor-keys": "6.20.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/utils": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.20.0.tgz", + "integrity": "sha512-/EKuw+kRu2vAqCoDwDCBtDRU6CTKbUmwwI7SH7AashZ+W+7o8eiyy6V2cdOqN49KsTcASWsC5QeghYuRDTyOOg==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.20.0", + "@typescript-eslint/types": "6.20.0", + "@typescript-eslint/typescript-estree": "6.20.0", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.20.0.tgz", + "integrity": "sha512-E8Cp98kRe4gKHjJD4NExXKz/zOJ1A2hhZc+IMVD6i7w4yjIvh6VyuRI0gRtxAsXtoC35uGMaQ9rjI2zJaXDEAw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.20.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", + "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vitest/coverage-v8": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-1.5.3.tgz", + "integrity": "sha512-DPyGSu/fPHOJuPxzFSQoT4N/Fu/2aJfZRtEpEp8GI7NHsXBGE94CQ+pbEGBUMFjatsHPDJw/+TAF9r4ens2CNw==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.1", + "@bcoe/v8-coverage": "^0.2.3", + "debug": "^4.3.4", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-report": "^3.0.1", + "istanbul-lib-source-maps": "^5.0.4", + "istanbul-reports": "^3.1.6", + "magic-string": "^0.30.5", + "magicast": "^0.3.3", + "picocolors": "^1.0.0", + "std-env": "^3.5.0", + "strip-literal": "^2.0.0", + "test-exclude": "^6.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "vitest": "1.5.3" + } + }, + "node_modules/@vitest/coverage-v8/node_modules/istanbul-lib-source-maps": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.4.tgz", + "integrity": "sha512-wHOoEsNJTVltaJp8eVkm8w+GVkVNHT2YDYo53YdzQEL2gWm1hBX5cGFR9hQJtuGLebidVX7et3+dmDZrmclduw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.23", + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@vitest/coverage-v8/node_modules/magic-string": { + "version": "0.30.10", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz", + "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + } + }, + "node_modules/@vitest/expect": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.5.3.tgz", + "integrity": "sha512-y+waPz31pOFr3rD7vWTbwiLe5+MgsMm40jTZbQE8p8/qXyBX3CQsIXRx9XK12IbY7q/t5a5aM/ckt33b4PxK2g==", + "dev": true, + "dependencies": { + "@vitest/spy": "1.5.3", + "@vitest/utils": "1.5.3", + "chai": "^4.3.10" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.5.3.tgz", + "integrity": "sha512-7PlfuReN8692IKQIdCxwir1AOaP5THfNkp0Uc4BKr2na+9lALNit7ub9l3/R7MP8aV61+mHKRGiqEKRIwu6iiQ==", + "dev": true, + "dependencies": { + "@vitest/utils": "1.5.3", + "p-limit": "^5.0.0", + "pathe": "^1.1.1" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner/node_modules/p-limit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-5.0.0.tgz", + "integrity": "sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@vitest/runner/node_modules/yocto-queue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", + "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@vitest/snapshot": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.5.3.tgz", + "integrity": "sha512-K3mvIsjyKYBhNIDujMD2gfQEzddLe51nNOAf45yKRt/QFJcUIeTQd2trRvv6M6oCBHNVnZwFWbQ4yj96ibiDsA==", + "dev": true, + "dependencies": { + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "pretty-format": "^29.7.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot/node_modules/magic-string": { + "version": "0.30.10", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz", + "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + } + }, + "node_modules/@vitest/spy": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.5.3.tgz", + "integrity": "sha512-Llj7Jgs6lbnL55WoshJUUacdJfjU2honvGcAJBxhra5TPEzTJH8ZuhI3p/JwqqfnTr4PmP7nDmOXP53MS7GJlg==", + "dev": true, + "dependencies": { + "tinyspy": "^2.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/ui": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-1.5.3.tgz", + "integrity": "sha512-DoSA5YxcUmeBEK7kJHzXiL2I0d9AijWI33arnUrwiWFDxgZPDxTjvSVsiXhe8qfqhloIHkwazl5E2rhlDd/ErA==", + "dev": true, + "dependencies": { + "@vitest/utils": "1.5.3", + "fast-glob": "^3.3.2", + "fflate": "^0.8.1", + "flatted": "^3.2.9", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "sirv": "^2.0.4" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "vitest": "1.5.3" + } + }, + "node_modules/@vitest/ui/node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/@vitest/ui/node_modules/mrmime": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz", + "integrity": "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/@vitest/ui/node_modules/sirv": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz", + "integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==", + "dev": true, + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@vitest/ui/node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/@vitest/utils": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.5.3.tgz", + "integrity": "sha512-rE9DTN1BRhzkzqNQO+kw8ZgfeEBCLXiHJwetk668shmNBpSagQxneT5eSqEBLP+cqSiAeecvQmbpFfdMyLcIQA==", + "dev": true, + "dependencies": { + "diff-sequences": "^29.6.3", + "estree-walker": "^3.0.3", + "loupe": "^2.3.7", + "pretty-format": "^29.7.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/@vscode/vsce": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-2.19.0.tgz", + "integrity": "sha512-dAlILxC5ggOutcvJY24jxz913wimGiUrHaPkk16Gm9/PGFbz1YezWtrXsTKUtJws4fIlpX2UIlVlVESWq8lkfQ==", + "dev": true, + "dependencies": { + "azure-devops-node-api": "^11.0.1", + "chalk": "^2.4.2", + "cheerio": "^1.0.0-rc.9", + "commander": "^6.1.0", + "glob": "^7.0.6", + "hosted-git-info": "^4.0.2", + "jsonc-parser": "^3.2.0", + "leven": "^3.1.0", + "markdown-it": "^12.3.2", + "mime": "^1.3.4", + "minimatch": "^3.0.3", + "parse-semver": "^1.1.1", + "read": "^1.0.7", + "semver": "^5.1.0", + "tmp": "^0.2.1", + "typed-rest-client": "^1.8.4", + "url-join": "^4.0.1", + "xml2js": "^0.5.0", + "yauzl": "^2.3.1", + "yazl": "^2.2.2" + }, + "bin": { + "vsce": "vsce" + }, + "engines": { + "node": ">= 14" + }, + "optionalDependencies": { + "keytar": "^7.7.0" + } + }, + "node_modules/@vscode/vsce/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/@vscode/vsce/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@vscode/vsce/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/@vscode/vsce/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/@vscode/vsce/node_modules/commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@vscode/vsce/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@vscode/vsce/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/@vscode/vsce/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", + "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==" + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==" + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", + "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==" + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", + "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==" + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", + "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", + "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", + "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==" + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", + "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-opt": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6", + "@webassemblyjs/wast-printer": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", + "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", + "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", + "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", + "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" + }, + "node_modules/@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", + "dev": true + }, + "node_modules/@yarnpkg/parsers": { + "version": "3.0.0-rc.46", + "resolved": "https://registry.npmjs.org/@yarnpkg/parsers/-/parsers-3.0.0-rc.46.tgz", + "integrity": "sha512-aiATs7pSutzda/rq8fnuPwTglyVwjM22bNnK2ZgjrpAjQHSSl3lztd2f9evst1W/qnC58DRz7T7QndUDumAR4Q==", + "dev": true, + "dependencies": { + "js-yaml": "^3.10.0", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=14.15.0" + } + }, + "node_modules/@zkochan/js-yaml": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@zkochan/js-yaml/-/js-yaml-0.0.6.tgz", + "integrity": "sha512-nzvgl3VfhcELQ8LyVrYOru+UtAy1nrygk2+AGbTm8a5YcO6o8lSjAT+pfg3vJWxIoZKOUhrK6UU7xW/+00kQrg==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@zkochan/js-yaml/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/abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "deprecated": "Use your platform's native atob() and btoa() methods instead", + "dev": true + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-globals": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", + "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", + "dev": true, + "dependencies": { + "acorn": "^8.1.0", + "acorn-walk": "^8.0.2" + } + }, + "node_modules/acorn-import-assertions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", + "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", + "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==", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", + "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/address": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/address/-/address-1.2.2.tgz", + "integrity": "sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/agentkeepalive": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.2.1.tgz", + "integrity": "sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA==", + "optional": true, + "dependencies": { + "debug": "^4.1.0", + "depd": "^1.1.2", + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/agentkeepalive/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "optional": true, + "engines": { + "node": ">= 0.6" + } + }, + "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==", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "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/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz", + "integrity": "sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/algoliasearch": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.17.2.tgz", + "integrity": "sha512-VFu43JJNYIW74awp7oeQcQsPcxOhd8psqBDTfyNO2Zt6L1NqnNMTVnaIdQ+8dtKqUDBqQZp0szPxECvX8CK2Fg==", + "dependencies": { + "@algolia/cache-browser-local-storage": "4.17.2", + "@algolia/cache-common": "4.17.2", + "@algolia/cache-in-memory": "4.17.2", + "@algolia/client-account": "4.17.2", + "@algolia/client-analytics": "4.17.2", + "@algolia/client-common": "4.17.2", + "@algolia/client-personalization": "4.17.2", + "@algolia/client-search": "4.17.2", + "@algolia/logger-common": "4.17.2", + "@algolia/logger-console": "4.17.2", + "@algolia/requester-browser-xhr": "4.17.2", + "@algolia/requester-common": "4.17.2", + "@algolia/requester-node-http": "4.17.2", + "@algolia/transporter": "4.17.2" + } + }, + "node_modules/algoliasearch-helper": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.13.1.tgz", + "integrity": "sha512-4ZUVmtMfe4+RgI1v53eGW4YQUsPJrZbi4AecGgmotkPXp0ysW4b2zG+/VpxgOz8cwwwkVLeEskL+iECzCwdpFw==", + "dependencies": { + "@algolia/events": "^4.0.1" + }, + "peerDependencies": { + "algoliasearch": ">= 3.1 < 6" + } + }, + "node_modules/ansi-align": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", + "dependencies": { + "string-width": "^4.1.0" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", + "engines": [ + "node >= 0.8.0" + ], + "bin": { + "ansi-html": "bin/ansi-html" + } + }, + "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/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" + }, + "node_modules/archiver": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz", + "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==", + "dependencies": { + "archiver-utils": "^2.1.0", + "async": "^3.2.4", + "buffer-crc32": "^0.2.1", + "readable-stream": "^3.6.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^2.2.0", + "zip-stream": "^4.1.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/archiver-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", + "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", + "dependencies": { + "glob": "^7.1.4", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^2.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/archiver-utils/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/archiver-utils/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/archiver-utils/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==" + }, + "node_modules/array-includes": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", + "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "get-intrinsic": "^1.1.3", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", + "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz", + "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.tosorted": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.1.tgz", + "integrity": "sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0", + "get-intrinsic": "^1.1.3" + } + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" + }, + "node_modules/assert": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-2.0.0.tgz", + "integrity": "sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==", + "dependencies": { + "es6-object-assign": "^1.1.0", + "is-nan": "^1.2.1", + "object-is": "^1.0.1", + "util": "^0.12.0" + } + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/ast-types-flow": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", + "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==", + "dev": true + }, + "node_modules/async": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/autoprefixer": { + "version": "10.4.13", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz", + "integrity": "sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + } + ], + "dependencies": { + "browserslist": "^4.21.4", + "caniuse-lite": "^1.0.30001426", + "fraction.js": "^4.2.0", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axe-core": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.6.0.tgz", + "integrity": "sha512-L3ZNbXPTxMrl0+qTXAzn9FBRvk5XdO56K8CvcCKtlxv44Aw2w2NCclGuvCWxHPw1Riiq3ncP/sxFYj2nUqdoTw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/axios": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz", + "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==", + "dev": true, + "dependencies": { + "follow-redirects": "^1.15.4", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axobject-query": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", + "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==", + "dev": true + }, + "node_modules/azure-devops-node-api": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-11.2.0.tgz", + "integrity": "sha512-XdiGPhrpaT5J8wdERRKs5g8E0Zy1pvOYTli7z9E8nmOn3YGp4FhtjhrOyFmX/8veWCwdI69mCHKJw6l+4J/bHA==", + "dev": true, + "dependencies": { + "tunnel": "0.0.6", + "typed-rest-client": "^1.8.4" + } + }, + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-loader": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.3.0.tgz", + "integrity": "sha512-H8SvsMF+m9t15HNLMipppzkC+Y2Yq+v3SonZyU70RBL/h1gxPkH08Ot8pEE9Z4Kd+czyWJClmFS8qzIP9OZ04Q==", + "dependencies": { + "find-cache-dir": "^3.3.1", + "loader-utils": "^2.0.0", + "make-dir": "^3.1.0", + "schema-utils": "^2.6.5" + }, + "engines": { + "node": ">= 8.9" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "webpack": ">=2" + } + }, + "node_modules/babel-plugin-apply-mdx-type-prop": { + "version": "1.6.22", + "resolved": "https://registry.npmjs.org/babel-plugin-apply-mdx-type-prop/-/babel-plugin-apply-mdx-type-prop-1.6.22.tgz", + "integrity": "sha512-VefL+8o+F/DfK24lPZMtJctrCVOfgbqLAGZSkxwhazQv4VxPg3Za/i40fu22KR2m8eEda+IfSOlPLUSIiLcnCQ==", + "dependencies": { + "@babel/helper-plugin-utils": "7.10.4", + "@mdx-js/util": "1.6.22" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@babel/core": "^7.11.6" + } + }, + "node_modules/babel-plugin-apply-mdx-type-prop/node_modules/@babel/helper-plugin-utils": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", + "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==" + }, + "node_modules/babel-plugin-const-enum": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-const-enum/-/babel-plugin-const-enum-1.2.0.tgz", + "integrity": "sha512-o1m/6iyyFnp9MRsK1dHF3bneqyf3AlM2q3A/YbgQr2pCat6B6XJVDv2TXqzfY2RYUi4mak6WAksSBPlyYGx9dg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-syntax-typescript": "^7.3.3", + "@babel/traverse": "^7.16.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "dependencies": { + "object.assign": "^4.1.0" + } + }, + "node_modules/babel-plugin-extract-import-names": { + "version": "1.6.22", + "resolved": "https://registry.npmjs.org/babel-plugin-extract-import-names/-/babel-plugin-extract-import-names-1.6.22.tgz", + "integrity": "sha512-yJ9BsJaISua7d8zNT7oRG1ZLBJCIdZ4PZqmH8qa9N5AK01ifk3fnkc98AXhtzE7UkfCsEumvoQWgoYLhOnJ7jQ==", + "dependencies": { + "@babel/helper-plugin-utils": "7.10.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/babel-plugin-extract-import-names/node_modules/@babel/helper-plugin-utils": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", + "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==" + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-plugin-macros": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz", + "integrity": "sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.7.2", + "cosmiconfig": "^6.0.0", + "resolve": "^1.12.0" + } + }, + "node_modules/babel-plugin-macros/node_modules/cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "dev": true, + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.8", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.8.tgz", + "integrity": "sha512-OtIuQfafSzpo/LhnJaykc0R/MMnuLSSVjVYy9mHArIZ9qTCSZ6TpWCuEKZYVoN//t8HqBNScHrOtCrIK5IaGLg==", + "dependencies": { + "@babel/compat-data": "^7.22.6", + "@babel/helper-define-polyfill-provider": "^0.5.0", + "semver": "^6.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.9.0.tgz", + "integrity": "sha512-7nZPG1uzK2Ymhy/NbaOWTg3uibM2BmGASS4vHS4szRZAIR8R6GwA/xAujpdrXU5iyklrimWnLWU+BLF9suPTqg==", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.5.0", + "core-js-compat": "^3.34.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.5.tgz", + "integrity": "sha512-OJGYZlhLqBh2DDHeqAxWB1XIvr49CxiJ2gIt61/PU55CQK4Z58OzMqjDe1zwQdQk+rBYsRc+1rJmdajM3gimHg==", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.5.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-transform-async-to-promises": { + "version": "0.8.18", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-to-promises/-/babel-plugin-transform-async-to-promises-0.8.18.tgz", + "integrity": "sha512-WpOrF76nUHijnNn10eBGOHZmXQC8JYRME9rOLxStOga7Av2VO53ehVFvVNImMksVtQuL2/7ZNxEgxnx7oo/3Hw==", + "dev": true + }, + "node_modules/babel-plugin-transform-react-remove-prop-types": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz", + "integrity": "sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA==", + "dev": true + }, + "node_modules/babel-plugin-transform-typescript-metadata": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-typescript-metadata/-/babel-plugin-transform-typescript-metadata-0.3.2.tgz", + "integrity": "sha512-mWEvCQTgXQf48yDqgN7CH50waTyYBeP2Lpqx4nNWab9sxEpdXVeKgfj1qYI2/TgUPQtNFZ85i3PemRtnXVYYJg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dev": true, + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-react-app": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-react-app/-/babel-preset-react-app-10.0.1.tgz", + "integrity": "sha512-b0D9IZ1WhhCWkrTXyFuIIgqGzSkRIH5D5AmB0bXbzYAB1OBAwHcUeyWW2LorutLWF5btNo/N7r/cIdmvvKJlYg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.16.0", + "@babel/plugin-proposal-class-properties": "^7.16.0", + "@babel/plugin-proposal-decorators": "^7.16.4", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.0", + "@babel/plugin-proposal-numeric-separator": "^7.16.0", + "@babel/plugin-proposal-optional-chaining": "^7.16.0", + "@babel/plugin-proposal-private-methods": "^7.16.0", + "@babel/plugin-transform-flow-strip-types": "^7.16.0", + "@babel/plugin-transform-react-display-name": "^7.16.0", + "@babel/plugin-transform-runtime": "^7.16.4", + "@babel/preset-env": "^7.16.4", + "@babel/preset-react": "^7.16.0", + "@babel/preset-typescript": "^7.16.0", + "@babel/runtime": "^7.16.3", + "babel-plugin-macros": "^3.1.0", + "babel-plugin-transform-react-remove-prop-types": "^0.4.24" + } + }, + "node_modules/babel-preset-react-app/node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, + "node_modules/bail": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz", + "integrity": "sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/base16": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/base16/-/base16-1.0.0.tgz", + "integrity": "sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ==" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "dev": true, + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/basic-auth/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/batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==" + }, + "node_modules/big-integer": { + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "engines": { + "node": "*" + } + }, + "node_modules/binary": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", + "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==", + "dependencies": { + "buffers": "~0.1.1", + "chainsaw": "~0.1.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/bonjour-service": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.0.14.tgz", + "integrity": "sha512-HIMbgLnk1Vqvs6B4Wq5ep7mxvj9sGz5d1JJyDNSGNIdA/w2MCz6GTjWTdjqOJV1bEPj+6IkxDvWNFKEBxNt4kQ==", + "dependencies": { + "array-flatten": "^2.1.2", + "dns-equal": "^1.0.0", + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" + }, + "node_modules/boxen": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-6.2.1.tgz", + "integrity": "sha512-H4PEsJXfFI/Pt8sjDWbHlQPx4zL/bvSQjcilJmaulGt5mLDorHOHpmdXAJcBcmru7PhYSp/cDMWRko4ZUMFkSw==", + "dependencies": { + "ansi-align": "^3.0.1", + "camelcase": "^6.2.0", + "chalk": "^4.1.2", + "cli-boxes": "^3.0.0", + "string-width": "^5.0.1", + "type-fest": "^2.5.0", + "widest-line": "^4.0.1", + "wrap-ansi": "^8.0.1" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/boxen/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/boxen/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/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==" + }, + "node_modules/boxen/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==", + "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/boxen/node_modules/strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/boxen/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==", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/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==", + "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/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.22.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.3.tgz", + "integrity": "sha512-UAp55yfwNv0klWNapjs/ktHoguxuQNGnOzxYmfnXIS+8AsRDZkSDxg7R1AX3GKzn078SBI5dzwzj/Yx0Or0e3A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001580", + "electron-to-chromium": "^1.4.648", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "engines": { + "node": "*" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/buffer-indexof-polyfill": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", + "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/buffer-writer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", + "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/buffers": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", + "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==", + "engines": { + "node": ">=0.2.0" + } + }, + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/builtins": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", + "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", + "dev": true, + "dependencies": { + "semver": "^7.0.0" + } + }, + "node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cacache": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", + "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "optional": true, + "dependencies": { + "@npmcli/fs": "^1.0.0", + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dependencies": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001581", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001581.tgz", + "integrity": "sha512-whlTkwhqV2tUmP3oYhtNfaWGYHDdS3JYFQBKXxcUR9qqPWsRhFHhoISO2Xnl/g0xyKzht9mI1LZpiNWfMzHixQ==", + "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==", + "dependencies": { + "lodash": "^4.17.15" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/ccount": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-1.1.0.tgz", + "integrity": "sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chai": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.4.1.tgz", + "integrity": "sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==", + "dev": true, + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.0.8" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chainsaw": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", + "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==", + "dependencies": { + "traverse": ">=0.3.0 <0.4" + }, + "engines": { + "node": "*" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "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/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/character-entities": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", + "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", + "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", + "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/cheerio": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", + "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "htmlparser2": "^8.0.1", + "parse5": "^7.0.0", + "parse5-htmlparser2-tree-adapter": "^7.0.0" + }, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cheerio-select/node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cheerio-select/node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/cheerio-select/node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/cheerio-select/node_modules/domutils": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz", + "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.1" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/cheerio-select/node_modules/entities": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", + "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/cheerio/node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/cheerio/node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/cheerio/node_modules/domutils": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz", + "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.1" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/cheerio/node_modules/entities": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", + "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/chevrotain": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-11.0.3.tgz", + "integrity": "sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw==", + "dependencies": { + "@chevrotain/cst-dts-gen": "11.0.3", + "@chevrotain/gast": "11.0.3", + "@chevrotain/regexp-to-ast": "11.0.3", + "@chevrotain/types": "11.0.3", + "@chevrotain/utils": "11.0.3", + "lodash-es": "4.17.21" + } + }, + "node_modules/chevrotain-allstar": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/chevrotain-allstar/-/chevrotain-allstar-0.3.1.tgz", + "integrity": "sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw==", + "dependencies": { + "lodash-es": "^4.17.21" + }, + "peerDependencies": { + "chevrotain": "^11.0.0" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "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" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/ci-info": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.5.0.tgz", + "integrity": "sha512-yH4RezKOGlOhxkmhbeNuC4eYZKAUsEaGtBuBzDDP1eFUKiccDWzBABxBfOx31IDwDIXMTxWuwAxUGModvkbuVw==" + }, + "node_modules/cjs-module-lexer": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", + "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", + "dev": true + }, + "node_modules/clean-css": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.2.tgz", + "integrity": "sha512-JVJbM+f3d3Q704rF4bqQ5UUyTtuJ0JRKNbTKVEeujCCBoMdkEi+V+e8oktO9qGQNSvHrFTM6JZRXrUvGR1czww==", + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 10.0" + } + }, + "node_modules/clean-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clean-regexp/-/clean-regexp-1.0.0.tgz", + "integrity": "sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=4" + } + }, + "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==", + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-boxes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", + "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", + "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-table3": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz", + "integrity": "sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==", + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "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/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clone-response/node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collapse-white-space": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.6.tgz", + "integrity": "sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "dev": true + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/colord": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==" + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==" + }, + "node_modules/columnify": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/columnify/-/columnify-1.6.0.tgz", + "integrity": "sha512-lomjuFZKfM6MSAnV9aCZC9sc0qGbmZdfygNv+nCpqVkSKdCxCklLtd16O0EILGkImHw9ZpHkAnHaB+8Zxq5W6Q==", + "dev": true, + "dependencies": { + "strip-ansi": "^6.0.1", + "wcwidth": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/combine-promises": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/combine-promises/-/combine-promises-1.1.0.tgz", + "integrity": "sha512-ZI9jvcLDxqwaXEixOhArm3r7ReIivsXkpbyEWyeOhzz1QS0iSgBPnWvEqvIQtYyamGCYA88gFhmUrs9hrrQ0pg==", + "engines": { + "node": ">=10" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/comma-separated-tokens": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz", + "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "engines": { + "node": ">= 12" + } + }, + "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==" + }, + "node_modules/compress-commons": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz", + "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==", + "dependencies": { + "buffer-crc32": "^0.2.13", + "crc32-stream": "^4.0.2", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dependencies": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/compression/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/concat-with-sourcemaps": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz", + "integrity": "sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg==", + "dev": true, + "dependencies": { + "source-map": "^0.6.1" + } + }, + "node_modules/confbox": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.7.tgz", + "integrity": "sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==", + "dev": true + }, + "node_modules/configstore": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", + "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", + "dependencies": { + "dot-prop": "^5.2.0", + "graceful-fs": "^4.1.2", + "make-dir": "^3.0.0", + "unique-string": "^2.0.0", + "write-file-atomic": "^3.0.0", + "xdg-basedir": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/configstore/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==", + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/confusing-browser-globals": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", + "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", + "dev": true + }, + "node_modules/connect-history-api-fallback": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/consola": { + "version": "2.15.3", + "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz", + "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==" + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "engines": { + "node": ">= 0.6" + } + }, + "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==" + }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/copy-anything": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz", + "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", + "dev": true, + "dependencies": { + "is-what": "^3.14.1" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/copy-text-to-clipboard": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/copy-text-to-clipboard/-/copy-text-to-clipboard-3.1.0.tgz", + "integrity": "sha512-PFM6BnjLnOON/lB3ta/Jg7Ywsv+l9kQGD4TWDCSlRBGmqnnTM5MrDkhAFgw+8HZt0wW6Q2BBE4cmy9sq+s9Qng==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/copy-webpack-plugin": { + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-10.2.4.tgz", + "integrity": "sha512-xFVltahqlsRcyyJqQbDY6EYTtyQZF9rf+JPjwHObLdPFMEISqkFkr7mFoVOC6BfYS/dNThyoQKvziugm+OnwBg==", + "dev": true, + "dependencies": { + "fast-glob": "^3.2.7", + "glob-parent": "^6.0.1", + "globby": "^12.0.2", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0" + }, + "engines": { + "node": ">= 12.20.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + } + }, + "node_modules/copy-webpack-plugin/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/copy-webpack-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/copy-webpack-plugin/node_modules/array-union": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-3.0.1.tgz", + "integrity": "sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/copy-webpack-plugin/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/copy-webpack-plugin/node_modules/globby": { + "version": "12.2.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-12.2.0.tgz", + "integrity": "sha512-wiSuFQLZ+urS9x2gGPl1H5drc5twabmm4m2gTR27XDFyjUHJUNsS8o/2aKyIF6IoBaR630atdher0XJ5g6OMmA==", + "dev": true, + "dependencies": { + "array-union": "^3.0.1", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.7", + "ignore": "^5.1.9", + "merge2": "^1.4.1", + "slash": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/copy-webpack-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/copy-webpack-plugin/node_modules/schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/copy-webpack-plugin/node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/core-js": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.26.1.tgz", + "integrity": "sha512-21491RRQVzUn0GGM9Z1Jrpr6PNPxPi+Za8OM9q4tksTSnlbXXGKK1nXNg/QvwFYettXvSX6zWKCtHHfjN4puyA==", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-compat": { + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.35.1.tgz", + "integrity": "sha512-sftHa5qUJY3rs9Zht1WEnmkvXputCyDBczPnr7QDgL8n3qrF3CMXY4VPSYtOLLiOUJcah2WNXREd48iOl6mQIw==", + "dependencies": { + "browserslist": "^4.22.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-pure": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.26.1.tgz", + "integrity": "sha512-VVXcDpp/xJ21KdULRq/lXdLzQAtX7+37LzpyfFM973il0tWSsDEoyzG38G14AjTpK9VTfiNM9jnFauq/CpaWGQ==", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "node_modules/corser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", + "integrity": "sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/cose-base": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz", + "integrity": "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==", + "dependencies": { + "layout-base": "^1.0.0" + } + }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "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/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/crc32-stream": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz", + "integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==", + "dependencies": { + "crc-32": "^1.2.0", + "readable-stream": "^3.4.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/cross-fetch": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.6.tgz", + "integrity": "sha512-riRvo06crlE8HiqOwIpQhxwdOk4fOeR7FVM/wXoxchFEqMNUjvbs3bfo4OTgMEMHzppd4DxFBDbyySj8Cv781g==", + "dependencies": { + "node-fetch": "^2.6.11" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/css-declaration-sorter": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.3.1.tgz", + "integrity": "sha512-fBffmak0bPAnyqc/HO8C3n2sHrp9wcqQz6ES9koRF2/mLOVAx9zIQ3Y7R29sYCteTPqMCwns4WYQoCX91Xl3+w==", + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.0.9" + } + }, + "node_modules/css-loader": { + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.2.tgz", + "integrity": "sha512-oqGbbVcBJkm8QwmnNzrFrWTnudnRZC+1eXikLJl0n4ljcfotgRifpg2a1lKy8jTrc4/d9A/ap1GFq1jDKG7J+Q==", + "dependencies": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.18", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.3.8" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/css-loader/node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/css-minimizer-webpack-plugin": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-5.0.1.tgz", + "integrity": "sha512-3caImjKFQkS+ws1TGcFn0V1HyDJFq1Euy589JlD6/3rV2kj+w7r5G9WDMgSHvpvXHNZ2calVypZWuEDQd9wfLg==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "cssnano": "^6.0.1", + "jest-worker": "^29.4.3", + "postcss": "^8.4.24", + "schema-utils": "^4.0.1", + "serialize-javascript": "^6.0.1" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@parcel/css": { + "optional": true + }, + "@swc/css": { + "optional": true + }, + "clean-css": { + "optional": true + }, + "csso": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "lightningcss": { + "optional": true + } + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/css-declaration-sorter": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.1.1.tgz", + "integrity": "sha512-dZ3bVTEEc1vxr3Bek9vGwfB5Z6ESPULhcRvO472mfjVnj8jRcTnKO8/JTczlvxM10Myb+wBM++1MtdO76eWcaQ==", + "dev": true, + "engines": { + "node": "^14 || ^16 || >=18" + }, + "peerDependencies": { + "postcss": "^8.0.9" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/css-tree": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "dev": true, + "dependencies": { + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/cssnano": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-6.0.3.tgz", + "integrity": "sha512-MRq4CIj8pnyZpcI2qs6wswoYoDD1t0aL28n+41c1Ukcpm56m1h6mCexIHBGjfZfnTqtGSSCP4/fB1ovxgjBOiw==", + "dev": true, + "dependencies": { + "cssnano-preset-default": "^6.0.3", + "lilconfig": "^3.0.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/cssnano" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/cssnano-preset-default": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-6.0.3.tgz", + "integrity": "sha512-4y3H370aZCkT9Ev8P4SO4bZbt+AExeKhh8wTbms/X7OLDo5E7AYUUy6YPxa/uF5Grf+AJwNcCnxKhZynJ6luBA==", + "dev": true, + "dependencies": { + "css-declaration-sorter": "^7.1.1", + "cssnano-utils": "^4.0.1", + "postcss-calc": "^9.0.1", + "postcss-colormin": "^6.0.2", + "postcss-convert-values": "^6.0.2", + "postcss-discard-comments": "^6.0.1", + "postcss-discard-duplicates": "^6.0.1", + "postcss-discard-empty": "^6.0.1", + "postcss-discard-overridden": "^6.0.1", + "postcss-merge-longhand": "^6.0.2", + "postcss-merge-rules": "^6.0.3", + "postcss-minify-font-values": "^6.0.1", + "postcss-minify-gradients": "^6.0.1", + "postcss-minify-params": "^6.0.2", + "postcss-minify-selectors": "^6.0.2", + "postcss-normalize-charset": "^6.0.1", + "postcss-normalize-display-values": "^6.0.1", + "postcss-normalize-positions": "^6.0.1", + "postcss-normalize-repeat-style": "^6.0.1", + "postcss-normalize-string": "^6.0.1", + "postcss-normalize-timing-functions": "^6.0.1", + "postcss-normalize-unicode": "^6.0.2", + "postcss-normalize-url": "^6.0.1", + "postcss-normalize-whitespace": "^6.0.1", + "postcss-ordered-values": "^6.0.1", + "postcss-reduce-initial": "^6.0.2", + "postcss-reduce-transforms": "^6.0.1", + "postcss-svgo": "^6.0.2", + "postcss-unique-selectors": "^6.0.2" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/cssnano-utils": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-4.0.1.tgz", + "integrity": "sha512-6qQuYDqsGoiXssZ3zct6dcMxiqfT6epy7x4R0TQJadd4LWO3sPR6JH6ZByOvVLoZ6EdwPGgd7+DR1EmX3tiXQQ==", + "dev": true, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/csso": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", + "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", + "dev": true, + "dependencies": { + "css-tree": "~2.2.0" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/csso/node_modules/css-tree": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", + "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", + "dev": true, + "dependencies": { + "mdn-data": "2.0.28", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/csso/node_modules/mdn-data": { + "version": "2.0.28", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", + "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", + "dev": true + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "dev": true, + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/css-minimizer-webpack-plugin/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/css-minimizer-webpack-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/lilconfig": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.0.0.tgz", + "integrity": "sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", + "dev": true + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/postcss-calc": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-9.0.1.tgz", + "integrity": "sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.11", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.2.2" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/postcss-colormin": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-6.0.2.tgz", + "integrity": "sha512-TXKOxs9LWcdYo5cgmcSHPkyrLAh86hX1ijmyy6J8SbOhyv6ua053M3ZAM/0j44UsnQNIWdl8gb5L7xX2htKeLw==", + "dev": true, + "dependencies": { + "browserslist": "^4.22.2", + "caniuse-api": "^3.0.0", + "colord": "^2.9.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/postcss-convert-values": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-6.0.2.tgz", + "integrity": "sha512-aeBmaTnGQ+NUSVQT8aY0sKyAD/BaLJenEKZ03YK0JnDE1w1Rr8XShoxdal2V2H26xTJKr3v5haByOhJuyT4UYw==", + "dev": true, + "dependencies": { + "browserslist": "^4.22.2", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/postcss-discard-comments": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-6.0.1.tgz", + "integrity": "sha512-f1KYNPtqYLUeZGCHQPKzzFtsHaRuECe6jLakf/RjSRqvF5XHLZnM2+fXLhb8Qh/HBFHs3M4cSLb1k3B899RYIg==", + "dev": true, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/postcss-discard-duplicates": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-6.0.1.tgz", + "integrity": "sha512-1hvUs76HLYR8zkScbwyJ8oJEugfPV+WchpnA+26fpJ7Smzs51CzGBHC32RS03psuX/2l0l0UKh2StzNxOrKCYg==", + "dev": true, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/postcss-discard-empty": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-6.0.1.tgz", + "integrity": "sha512-yitcmKwmVWtNsrrRqGJ7/C0YRy53i0mjexBDQ9zYxDwTWVBgbU4+C9jIZLmQlTDT9zhml+u0OMFJh8+31krmOg==", + "dev": true, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/postcss-discard-overridden": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-6.0.1.tgz", + "integrity": "sha512-qs0ehZMMZpSESbRkw1+inkf51kak6OOzNRaoLd/U7Fatp0aN2HQ1rxGOrJvYcRAN9VpX8kUF13R2ofn8OlvFVA==", + "dev": true, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/postcss-merge-longhand": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-6.0.2.tgz", + "integrity": "sha512-+yfVB7gEM8SrCo9w2lCApKIEzrTKl5yS1F4yGhV3kSim6JzbfLGJyhR1B6X+6vOT0U33Mgx7iv4X9MVWuaSAfw==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0", + "stylehacks": "^6.0.2" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/postcss-merge-rules": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-6.0.3.tgz", + "integrity": "sha512-yfkDqSHGohy8sGYIJwBmIGDv4K4/WrJPX355XrxQb/CSsT4Kc/RxDi6akqn5s9bap85AWgv21ArcUWwWdGNSHA==", + "dev": true, + "dependencies": { + "browserslist": "^4.22.2", + "caniuse-api": "^3.0.0", + "cssnano-utils": "^4.0.1", + "postcss-selector-parser": "^6.0.15" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/postcss-minify-font-values": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-6.0.1.tgz", + "integrity": "sha512-tIwmF1zUPoN6xOtA/2FgVk1ZKrLcCvE0dpZLtzyyte0j9zUeB8RTbCqrHZGjJlxOvNWKMYtunLrrl7HPOiR46w==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/postcss-minify-gradients": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-6.0.1.tgz", + "integrity": "sha512-M1RJWVjd6IOLPl1hYiOd5HQHgpp6cvJVLrieQYS9y07Yo8itAr6jaekzJphaJFR0tcg4kRewCk3kna9uHBxn/w==", + "dev": true, + "dependencies": { + "colord": "^2.9.1", + "cssnano-utils": "^4.0.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/postcss-minify-params": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-6.0.2.tgz", + "integrity": "sha512-zwQtbrPEBDj+ApELZ6QylLf2/c5zmASoOuA4DzolyVGdV38iR2I5QRMsZcHkcdkZzxpN8RS4cN7LPskOkTwTZw==", + "dev": true, + "dependencies": { + "browserslist": "^4.22.2", + "cssnano-utils": "^4.0.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/postcss-minify-selectors": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-6.0.2.tgz", + "integrity": "sha512-0b+m+w7OAvZejPQdN2GjsXLv5o0jqYHX3aoV0e7RBKPCsB7TYG5KKWBFhGnB/iP3213Ts8c5H4wLPLMm7z28Sg==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.15" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/postcss-normalize-charset": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-6.0.1.tgz", + "integrity": "sha512-aW5LbMNRZ+oDV57PF9K+WI1Z8MPnF+A8qbajg/T8PP126YrGX1f9IQx21GI2OlGz7XFJi/fNi0GTbY948XJtXg==", + "dev": true, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/postcss-normalize-display-values": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-6.0.1.tgz", + "integrity": "sha512-mc3vxp2bEuCb4LgCcmG1y6lKJu1Co8T+rKHrcbShJwUmKJiEl761qb/QQCfFwlrvSeET3jksolCR/RZuMURudw==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/postcss-normalize-positions": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-6.0.1.tgz", + "integrity": "sha512-HRsq8u/0unKNvm0cvwxcOUEcakFXqZ41fv3FOdPn916XFUrympjr+03oaLkuZENz3HE9RrQE9yU0Xv43ThWjQg==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/postcss-normalize-repeat-style": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-6.0.1.tgz", + "integrity": "sha512-Gbb2nmCy6tTiA7Sh2MBs3fj9W8swonk6lw+dFFeQT68B0Pzwp1kvisJQkdV6rbbMSd9brMlS8I8ts52tAGWmGQ==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/postcss-normalize-string": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-6.0.1.tgz", + "integrity": "sha512-5Fhx/+xzALJD9EI26Aq23hXwmv97Zfy2VFrt5PLT8lAhnBIZvmaT5pQk+NuJ/GWj/QWaKSKbnoKDGLbV6qnhXg==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/postcss-normalize-timing-functions": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-6.0.1.tgz", + "integrity": "sha512-4zcczzHqmCU7L5dqTB9rzeqPWRMc0K2HoR+Bfl+FSMbqGBUcP5LRfgcH4BdRtLuzVQK1/FHdFoGT3F7rkEnY+g==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/postcss-normalize-unicode": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-6.0.2.tgz", + "integrity": "sha512-Ff2VdAYCTGyMUwpevTZPZ4w0+mPjbZzLLyoLh/RMpqUqeQKZ+xMm31hkxBavDcGKcxm6ACzGk0nBfZ8LZkStKA==", + "dev": true, + "dependencies": { + "browserslist": "^4.22.2", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/postcss-normalize-url": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-6.0.1.tgz", + "integrity": "sha512-jEXL15tXSvbjm0yzUV7FBiEXwhIa9H88JOXDGQzmcWoB4mSjZIsmtto066s2iW9FYuIrIF4k04HA2BKAOpbsaQ==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/postcss-normalize-whitespace": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-6.0.1.tgz", + "integrity": "sha512-76i3NpWf6bB8UHlVuLRxG4zW2YykF9CTEcq/9LGAiz2qBuX5cBStadkk0jSkg9a9TCIXbMQz7yzrygKoCW9JuA==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/postcss-ordered-values": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-6.0.1.tgz", + "integrity": "sha512-XXbb1O/MW9HdEhnBxitZpPFbIvDgbo9NK4c/5bOfiKpnIGZDoL2xd7/e6jW5DYLsWxBbs+1nZEnVgnjnlFViaA==", + "dev": true, + "dependencies": { + "cssnano-utils": "^4.0.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/postcss-reduce-initial": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-6.0.2.tgz", + "integrity": "sha512-YGKalhNlCLcjcLvjU5nF8FyeCTkCO5UtvJEt0hrPZVCTtRLSOH4z00T1UntQPj4dUmIYZgMj8qK77JbSX95hSw==", + "dev": true, + "dependencies": { + "browserslist": "^4.22.2", + "caniuse-api": "^3.0.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/postcss-reduce-transforms": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-6.0.1.tgz", + "integrity": "sha512-fUbV81OkUe75JM+VYO1gr/IoA2b/dRiH6HvMwhrIBSUrxq3jNZQZitSnugcTLDi1KkQh1eR/zi+iyxviUNBkcQ==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/postcss-svgo": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-6.0.2.tgz", + "integrity": "sha512-IH5R9SjkTkh0kfFOQDImyy1+mTCb+E830+9SV1O+AaDcoHTvfsvt6WwJeo7KwcHbFnevZVCsXhDmjFiGVuwqFQ==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0", + "svgo": "^3.2.0" + }, + "engines": { + "node": "^14 || ^16 || >= 18" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/postcss-unique-selectors": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-6.0.2.tgz", + "integrity": "sha512-8IZGQ94nechdG7Y9Sh9FlIY2b4uS8/k8kdKRX040XHsS3B6d1HrJAkXrBSsSu4SuARruSsUjW3nlSw8BHkaAYQ==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.15" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/stylehacks": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-6.0.2.tgz", + "integrity": "sha512-00zvJGnCu64EpMjX8b5iCZ3us2Ptyw8+toEkb92VdmkEaRaSGBNKAoK6aWZckhXxmQP8zWiTaFaiMGIU8Ve8sg==", + "dev": true, + "dependencies": { + "browserslist": "^4.22.2", + "postcss-selector-parser": "^6.0.15" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/svgo": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.2.0.tgz", + "integrity": "sha512-4PP6CMW/V7l/GmKRKzsLR8xxjdHTV4IMvhTnpuHwwBazSIlw5W/5SmPjN8Dwyt7lKbSJrRDgp4t9ph0HgChFBQ==", + "dev": true, + "dependencies": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^5.1.0", + "css-tree": "^2.3.1", + "css-what": "^6.1.0", + "csso": "^5.0.5", + "picocolors": "^1.0.0" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/svgo" + } + }, + "node_modules/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "dependencies": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssnano": { + "version": "5.1.14", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.14.tgz", + "integrity": "sha512-Oou7ihiTocbKqi0J1bB+TRJIQX5RMR3JghA8hcWSw9mjBLQ5Y3RWqEDoYG3sRNlAbCIXpqMoZGbq5KDR3vdzgw==", + "dependencies": { + "cssnano-preset-default": "^5.2.13", + "lilconfig": "^2.0.3", + "yaml": "^1.10.2" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/cssnano" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/cssnano-preset-advanced": { + "version": "5.3.10", + "resolved": "https://registry.npmjs.org/cssnano-preset-advanced/-/cssnano-preset-advanced-5.3.10.tgz", + "integrity": "sha512-fnYJyCS9jgMU+cmHO1rPSPf9axbQyD7iUhLO5Df6O4G+fKIOMps+ZbU0PdGFejFBBZ3Pftf18fn1eG7MAPUSWQ==", + "dependencies": { + "autoprefixer": "^10.4.12", + "cssnano-preset-default": "^5.2.14", + "postcss-discard-unused": "^5.1.0", + "postcss-merge-idents": "^5.1.1", + "postcss-reduce-idents": "^5.2.0", + "postcss-zindex": "^5.1.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/cssnano-preset-default": { + "version": "5.2.14", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.14.tgz", + "integrity": "sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A==", + "dependencies": { + "css-declaration-sorter": "^6.3.1", + "cssnano-utils": "^3.1.0", + "postcss-calc": "^8.2.3", + "postcss-colormin": "^5.3.1", + "postcss-convert-values": "^5.1.3", + "postcss-discard-comments": "^5.1.2", + "postcss-discard-duplicates": "^5.1.0", + "postcss-discard-empty": "^5.1.1", + "postcss-discard-overridden": "^5.1.0", + "postcss-merge-longhand": "^5.1.7", + "postcss-merge-rules": "^5.1.4", + "postcss-minify-font-values": "^5.1.0", + "postcss-minify-gradients": "^5.1.1", + "postcss-minify-params": "^5.1.4", + "postcss-minify-selectors": "^5.2.1", + "postcss-normalize-charset": "^5.1.0", + "postcss-normalize-display-values": "^5.1.0", + "postcss-normalize-positions": "^5.1.1", + "postcss-normalize-repeat-style": "^5.1.1", + "postcss-normalize-string": "^5.1.0", + "postcss-normalize-timing-functions": "^5.1.0", + "postcss-normalize-unicode": "^5.1.1", + "postcss-normalize-url": "^5.1.0", + "postcss-normalize-whitespace": "^5.1.1", + "postcss-ordered-values": "^5.1.3", + "postcss-reduce-initial": "^5.1.2", + "postcss-reduce-transforms": "^5.1.0", + "postcss-svgo": "^5.1.0", + "postcss-unique-selectors": "^5.1.1" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/cssnano-utils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz", + "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/csso": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "dependencies": { + "css-tree": "^1.1.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/cssom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", + "dev": true + }, + "node_modules/cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "dependencies": { + "cssom": "~0.3.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + }, + "node_modules/csstype": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", + "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" + }, + "node_modules/cytoscape": { + "version": "3.25.0", + "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.25.0.tgz", + "integrity": "sha512-7MW3Iz57mCUo6JQCho6CmPBCbTlJr7LzyEtIkutG255HLVd4XuBg2I9BkTZLI/e4HoaOB/BiAzXuQybQ95+r9Q==", + "dependencies": { + "heap": "^0.2.6", + "lodash": "^4.17.21" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/cytoscape-cose-bilkent": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz", + "integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==", + "dependencies": { + "cose-base": "^1.0.0" + }, + "peerDependencies": { + "cytoscape": "^3.2.0" + } + }, + "node_modules/cytoscape-fcose": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz", + "integrity": "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==", + "dependencies": { + "cose-base": "^2.2.0" + }, + "peerDependencies": { + "cytoscape": "^3.2.0" + } + }, + "node_modules/cytoscape-fcose/node_modules/cose-base": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-2.2.0.tgz", + "integrity": "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==", + "dependencies": { + "layout-base": "^2.0.0" + } + }, + "node_modules/cytoscape-fcose/node_modules/layout-base": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-2.0.1.tgz", + "integrity": "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==" + }, + "node_modules/d3": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.8.5.tgz", + "integrity": "sha512-JgoahDG51ncUfJu6wX/1vWQEqOflgXyl4MaHqlcSruTez7yhaRKR9i8VjjcQGeS2en/jnFivXuaIMnseMMt0XA==", + "dependencies": { + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "4", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-axis": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-brush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-chord": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", + "dependencies": { + "d3-path": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-contour": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", + "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", + "dependencies": { + "d3-array": "^3.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", + "dependencies": { + "delaunator": "5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "dependencies": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, + "bin": { + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", + "dependencies": { + "d3-dsv": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.0.tgz", + "integrity": "sha512-JEo5HxXDdDYXCaWdwLRt79y7giK8SbhZJbFWXqbRTolCHFI5jRqteLzCsq51NKbUoX0PjBVSohxrx+NoOUujYA==", + "dependencies": { + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-polygon": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", + "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale-chromatic": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz", + "integrity": "sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g==", + "dependencies": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dagre-d3-es": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.9.tgz", + "integrity": "sha512-rYR4QfVmy+sR44IBDvVtcAmOReGBvRCWDpO2QjYwqgh9yijw6eSHBqaPG/LIOEy7aBsniLvtMW6pg19qJhq60w==", + "dependencies": { + "d3": "^7.8.2", + "lodash-es": "^4.17.21" + } + }, + "node_modules/damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", + "dev": true + }, + "node_modules/data-urls": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", + "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", + "dev": true, + "dependencies": { + "abab": "^2.0.6", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dayjs": { + "version": "1.11.8", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.8.tgz", + "integrity": "sha512-LcgxzFoWMEPO7ggRv1Y2N31hUf2R0Vj7fuy/m+Bg1K8rr+KAs1AEy4y9jd5DXe8pbHgX+srkHNS7TH6Q6ZhYeQ==" + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", + "dev": true + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dedent": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", + "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", + "dev": true, + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deep-eql": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", + "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", + "dev": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "engines": { + "node": ">=4.0.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==" + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-gateway": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "engines": { + "node": ">=10" + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "engines": { + "node": ">=8" + } + }, + "node_modules/define-properties": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "dependencies": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/del": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-6.1.1.tgz", + "integrity": "sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==", + "dependencies": { + "globby": "^11.0.1", + "graceful-fs": "^4.2.4", + "is-glob": "^4.0.1", + "is-path-cwd": "^2.2.0", + "is-path-inside": "^3.0.2", + "p-map": "^4.0.0", + "rimraf": "^3.0.2", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/del/node_modules/fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/del/node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/delaunator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.0.tgz", + "integrity": "sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==", + "dependencies": { + "robust-predicates": "^3.0.0" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detab": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detab/-/detab-2.0.4.tgz", + "integrity": "sha512-8zdsQA5bIkoRECvCrNKPla84lyoR7DSAyf7p0YgXzBO9PDJx8KntPUay7NS6yp+KdxdVtiE5SpHKtbp2ZQyA9g==", + "dependencies": { + "repeat-string": "^1.5.4" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/detect-libc": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", + "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==" + }, + "node_modules/detect-port": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.5.1.tgz", + "integrity": "sha512-aBzdj76lueB6uUst5iAs7+0H/oOjqI5D16XUWxlWMIMROhcM0rfsNVk93zTngq1dDNpoXRr++Sus7ETAExppAQ==", + "dependencies": { + "address": "^1.0.1", + "debug": "4" + }, + "bin": { + "detect": "bin/detect-port.js", + "detect-port": "bin/detect-port.js" + } + }, + "node_modules/detect-port-alt": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz", + "integrity": "sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==", + "dependencies": { + "address": "^1.0.1", + "debug": "^2.6.0" + }, + "bin": { + "detect": "bin/detect-port", + "detect-port": "bin/detect-port" + }, + "engines": { + "node": ">= 4.2.1" + } + }, + "node_modules/detect-port-alt/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/detect-port-alt/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==" + }, + "node_modules/dns-packet": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.4.0.tgz", + "integrity": "sha512-EgqGeaBB8hLiHLZtp/IbaDQTL8pZ0+IvwzSHA6d7VyMDM+B9hgddEMa9xjK5oYnw0ci0JQ6g2XCD7/f6cafU6g==", + "dependencies": { + "@leichtgewicht/ip-codec": "^2.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "dependencies": { + "utila": "~0.4" + } + }, + "node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domexception": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", + "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", + "deprecated": "Use your platform's native DOMException instead", + "dev": true, + "dependencies": { + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/dompurify": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.3.tgz", + "integrity": "sha512-q6QaLcakcRjebxjg8/+NP+h0rPfatOgOzc46Fst9VAA3jF2ApfKBNKMzdP4DYTqtUMXSCd5pRS/8Po/OmoCHZQ==" + }, + "node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dot-prop/node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/dotenv": { + "version": "16.3.2", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.2.tgz", + "integrity": "sha512-HTlk5nmhkm8F6JcdXvHIzaorzCoziNQT9mGxLPVXW8wJF1TiGSL60ZGB4gHWabHOaMmWmhvk2/lPHfnBiT78AQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/motdotla/dotenv?sponsor=1" + } + }, + "node_modules/dotenv-expand": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-10.0.0.tgz", + "integrity": "sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" + }, + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "dependencies": { + "readable-stream": "^2.0.2" + } + }, + "node_modules/duplexer2/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/duplexer2/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/duplexer2/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/ejs": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.8.tgz", + "integrity": "sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==", + "dev": true, + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.651", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.651.tgz", + "integrity": "sha512-jjks7Xx+4I7dslwsbaFocSwqBbGHQmuXBJUK9QBZTIrzPq3pzn6Uf2szFSP728FtLYE3ldiccmlkOM/zhGKCpA==" + }, + "node_modules/elkjs": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/elkjs/-/elkjs-0.8.2.tgz", + "integrity": "sha512-L6uRgvZTH+4OF5NE/MBbzQx/WYpru1xCBE9respNj6qznEewGUIfhzmm7horWWxbNO2M0WckQypGctR8lH79xQ==" + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/emoticon": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/emoticon/-/emoticon-3.2.0.tgz", + "integrity": "sha512-SNujglcLTTg+lDAcApPNgEdudaqQFiAbJCqzjNxJkvN9vAwCGi0uu8IUVvx+f16h+V44KCY6Y2yboroc9pilHg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", + "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "optional": true + }, + "node_modules/errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dev": true, + "optional": true, + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.4.tgz", + "integrity": "sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.1.3", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.2", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.4.3", + "safe-regex-test": "^1.0.0", + "string.prototype.trimend": "^1.0.5", + "string.prototype.trimstart": "^1.0.5", + "unbox-primitive": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-module-lexer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.4.1.tgz", + "integrity": "sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==" + }, + "node_modules/es-shim-unscopables": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", + "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es6-object-assign": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", + "integrity": "sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw==" + }, + "node_modules/esbuild": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", + "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.19.12", + "@esbuild/android-arm": "0.19.12", + "@esbuild/android-arm64": "0.19.12", + "@esbuild/android-x64": "0.19.12", + "@esbuild/darwin-arm64": "0.19.12", + "@esbuild/darwin-x64": "0.19.12", + "@esbuild/freebsd-arm64": "0.19.12", + "@esbuild/freebsd-x64": "0.19.12", + "@esbuild/linux-arm": "0.19.12", + "@esbuild/linux-arm64": "0.19.12", + "@esbuild/linux-ia32": "0.19.12", + "@esbuild/linux-loong64": "0.19.12", + "@esbuild/linux-mips64el": "0.19.12", + "@esbuild/linux-ppc64": "0.19.12", + "@esbuild/linux-riscv64": "0.19.12", + "@esbuild/linux-s390x": "0.19.12", + "@esbuild/linux-x64": "0.19.12", + "@esbuild/netbsd-x64": "0.19.12", + "@esbuild/openbsd-x64": "0.19.12", + "@esbuild/sunos-x64": "0.19.12", + "@esbuild/win32-arm64": "0.19.12", + "@esbuild/win32-ia32": "0.19.12", + "@esbuild/win32-x64": "0.19.12" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-goat": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", + "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dev": true, + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/eslint": { + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.48.0.tgz", + "integrity": "sha512-sb6DLeIuRXxeM1YljSe1KEx9/YYeZFQWcV8Rq9HfigmdDEugjLEVEa1ozDjL6YDjBpQHPJxJzze+alxi4T3OLg==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "8.48.0", + "@humanwhocodes/config-array": "^0.11.10", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-prettier": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-config-react-app": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-7.0.1.tgz", + "integrity": "sha512-K6rNzvkIeHaTd8m/QEh1Zko0KI7BACWkkneSs6s9cKZC/J27X3eZR6Upt1jkmZ/4FK+XUOPPxMEN7+lbUXfSlA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.16.0", + "@babel/eslint-parser": "^7.16.3", + "@rushstack/eslint-patch": "^1.1.0", + "@typescript-eslint/eslint-plugin": "^5.5.0", + "@typescript-eslint/parser": "^5.5.0", + "babel-preset-react-app": "^10.0.1", + "confusing-browser-globals": "^1.0.11", + "eslint-plugin-flowtype": "^8.0.3", + "eslint-plugin-import": "^2.25.3", + "eslint-plugin-jest": "^25.3.0", + "eslint-plugin-jsx-a11y": "^6.5.1", + "eslint-plugin-react": "^7.27.1", + "eslint-plugin-react-hooks": "^4.3.0", + "eslint-plugin-testing-library": "^5.0.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "eslint": "^8.0.0" + } + }, + "node_modules/eslint-config-react-app/node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", + "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/type-utils": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-config-react-app/node_modules/@typescript-eslint/parser": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", + "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-config-react-app/node_modules/@typescript-eslint/type-utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", + "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-config-react-app/node_modules/eslint-plugin-jest": { + "version": "25.7.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-25.7.0.tgz", + "integrity": "sha512-PWLUEXeeF7C9QGKqvdSbzLOiLTx+bno7/HC9eefePfEb257QFHg7ye3dh80AZVkaa/RQsBB1Q/ORQvg2X7F0NQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/experimental-utils": "^5.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^4.0.0 || ^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + }, + "jest": { + "optional": true + } + } + }, + "node_modules/eslint-config-react-app/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz", + "integrity": "sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.11.0", + "resolve": "^1.22.1" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz", + "integrity": "sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==", + "dev": true, + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-flowtype": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-8.0.3.tgz", + "integrity": "sha512-dX8l6qUL6O+fYPtpNRideCFSpmWOUVx5QcaGLVqe/vlDiBSe4vYljDWDETwnyFzpl7By/WVIu6rcrniCgH9BqQ==", + "dev": true, + "dependencies": { + "lodash": "^4.17.21", + "string-natural-compare": "^3.0.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@babel/plugin-syntax-flow": "^7.14.5", + "@babel/plugin-transform-react-jsx": "^7.14.9", + "eslint": "^8.1.0" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.27.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz", + "integrity": "sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "array.prototype.flatmap": "^1.3.1", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.7", + "eslint-module-utils": "^2.7.4", + "has": "^1.0.3", + "is-core-module": "^2.11.0", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.values": "^1.1.6", + "resolve": "^1.22.1", + "semver": "^6.3.0", + "tsconfig-paths": "^3.14.1" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/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-plugin-import/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-jsx-a11y": { + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.6.1.tgz", + "integrity": "sha512-sXgFVNHiWffBq23uiS/JaP6eVR622DqwB4yTzKvGZGcPq6/yZ3WmOZfuBks/vHWo9GaFOqC2ZK4i6+C35knx7Q==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.18.9", + "aria-query": "^4.2.2", + "array-includes": "^3.1.5", + "ast-types-flow": "^0.0.7", + "axe-core": "^4.4.3", + "axobject-query": "^2.2.0", + "damerau-levenshtein": "^1.0.8", + "emoji-regex": "^9.2.2", + "has": "^1.0.3", + "jsx-ast-utils": "^3.3.2", + "language-tags": "^1.0.5", + "minimatch": "^3.1.2", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=4.0" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + } + }, + "node_modules/eslint-plugin-jsx-a11y/node_modules/aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.10.2", + "@babel/runtime-corejs3": "^7.10.2" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/eslint-plugin-jsx-a11y/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/eslint-plugin-jsx-a11y/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-plugin-jsx-a11y/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz", + "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "eslint": ">=7.28.0", + "prettier": ">=2.0.0" + }, + "peerDependenciesMeta": { + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react": { + "version": "7.32.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.32.2.tgz", + "integrity": "sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flatmap": "^1.3.1", + "array.prototype.tosorted": "^1.1.1", + "doctrine": "^2.1.0", + "estraverse": "^5.3.0", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.6", + "object.fromentries": "^2.0.6", + "object.hasown": "^1.1.2", + "object.values": "^1.1.6", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.4", + "semver": "^6.3.0", + "string.prototype.matchall": "^4.0.8" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", + "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", + "dev": true, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/eslint-plugin-react/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-react/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-plugin-react/node_modules/resolve": { + "version": "2.0.0-next.4", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", + "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==", + "dev": true, + "dependencies": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/eslint-plugin-react/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-testing-library": { + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-5.9.1.tgz", + "integrity": "sha512-6BQp3tmb79jLLasPHJmy8DnxREe+2Pgf7L+7o09TSWPfdqqtQfRZmZNetr5mOs3yqZk/MRNxpN3RUpJe0wB4LQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/utils": "^5.13.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0", + "npm": ">=6" + }, + "peerDependencies": { + "eslint": "^7.5.0 || ^8.0.0" + } + }, + "node_modules/eslint-plugin-unicorn": { + "version": "52.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-52.0.0.tgz", + "integrity": "sha512-1Yzm7/m+0R4djH0tjDjfVei/ju2w3AzUGjG6q8JnuNIL5xIwsflyCooW5sfBvQp2pMYQFSWWCFONsjCax1EHng==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "@eslint-community/eslint-utils": "^4.4.0", + "@eslint/eslintrc": "^2.1.4", + "ci-info": "^4.0.0", + "clean-regexp": "^1.0.0", + "core-js-compat": "^3.34.0", + "esquery": "^1.5.0", + "indent-string": "^4.0.0", + "is-builtin-module": "^3.2.1", + "jsesc": "^3.0.2", + "pluralize": "^8.0.0", + "read-pkg-up": "^7.0.1", + "regexp-tree": "^0.1.27", + "regjsparser": "^0.10.0", + "semver": "^7.5.4", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1" + }, + "peerDependencies": { + "eslint": ">=8.56.0" + } + }, + "node_modules/eslint-plugin-unicorn/node_modules/ci-info": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.0.0.tgz", + "integrity": "sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint-plugin-unicorn/node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/eslint-plugin-unicorn/node_modules/regjsparser": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.10.0.tgz", + "integrity": "sha512-qx+xQGZVsy55CH0a1hiVwHmqjLryfh7wQyF5HO07XJ9f7dQMY/gPQHhlyDkIzJKC+x2fUCpCcUODUUUFrm7SHA==", + "dev": true, + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/eslint-plugin-unicorn/node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + } + }, + "node_modules/eslint-plugin-unicorn/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-plugin-vitest": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-vitest/-/eslint-plugin-vitest-0.5.4.tgz", + "integrity": "sha512-um+odCkccAHU53WdKAw39MY61+1x990uXjSPguUCq3VcEHdqJrOb8OTMrbYlY6f9jAKx7x98kLVlIe3RJeJqoQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/utils": "^7.7.1" + }, + "engines": { + "node": "^18.0.0 || >= 20.0.0" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "vitest": "*" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + }, + "vitest": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/scope-manager": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.8.0.tgz", + "integrity": "sha512-viEmZ1LmwsGcnr85gIq+FCYI7nO90DVbE37/ll51hjv9aG+YZMb4WDE2fyWpUR4O/UrhGRpYXK/XajcGTk2B8g==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.8.0", + "@typescript-eslint/visitor-keys": "7.8.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/types": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.8.0.tgz", + "integrity": "sha512-wf0peJ+ZGlcH+2ZS23aJbOv+ztjeeP8uQ9GgwMJGVLx/Nj9CJt17GWgWWoSmoRVKAX2X+7fzEnAjxdvK2gqCLw==", + "dev": true, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/typescript-estree": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.8.0.tgz", + "integrity": "sha512-5pfUCOwK5yjPaJQNy44prjCwtr981dO8Qo9J9PwYXZ0MosgAbfEMB008dJ5sNo3+/BN6ytBPuSvXUg9SAqB0dg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.8.0", + "@typescript-eslint/visitor-keys": "7.8.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/utils": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.8.0.tgz", + "integrity": "sha512-L0yFqOCflVqXxiZyXrDr80lnahQfSOfc9ELAAZ75sqicqp2i36kEZZGuUymHNFoYOqxRT05up760b4iGsl02nQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.15", + "@types/semver": "^7.5.8", + "@typescript-eslint/scope-manager": "7.8.0", + "@typescript-eslint/types": "7.8.0", + "@typescript-eslint/typescript-estree": "7.8.0", + "semver": "^7.6.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + } + }, + "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/visitor-keys": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.8.0.tgz", + "integrity": "sha512-q4/gibTNBQNA0lGyYQCmWRS5D15n8rXh4QjK3KV+MBPlTYHpfBUT3D3PaPR/HeNiI9W6R7FvlkcGhNyAoP+caA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.8.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-plugin-vitest/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/eslint-plugin-vitest/node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/eslint-plugin-vitest/node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-plugin-vitest/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/eslint-plugin-vitest/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-scope/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "engines": { + "node": ">=4.0" + } + }, + "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": "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==", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/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/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/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/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/globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/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/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-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/eslint/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "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==", + "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==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eta": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/eta/-/eta-2.0.1.tgz", + "integrity": "sha512-46E2qDPDm7QA+usjffUWz9KfXsxVZclPOuKsXs4ZWZdI/X1wpDF7AO424pt7fdYohCzWsIkXAhNGXSlwo5naAg==", + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "url": "https://github.com/eta-dev/eta?sponsor=1" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eval": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/eval/-/eval-0.1.8.tgz", + "integrity": "sha512-EzV94NYKoO09GLXGjXj9JIlXijVck4ONSr5wiCWDvhsvj5jxSrzTmRU/9C1DyB6uToszLs8aifA6NQ7lEQdvFw==", + "dependencies": { + "@types/node": "*", + "require-like": ">= 0.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/exceljs": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/exceljs/-/exceljs-4.3.0.tgz", + "integrity": "sha512-hTAeo5b5TPvf8Z02I2sKIT4kSfCnOO2bCxYX8ABqODCdAjppI3gI9VYiGCQQYVcBaBSKlFDMKlAQRqC+kV9O8w==", + "dependencies": { + "archiver": "^5.0.0", + "dayjs": "^1.8.34", + "fast-csv": "^4.3.1", + "jszip": "^3.5.0", + "readable-stream": "^3.6.0", + "saxes": "^5.0.1", + "tmp": "^0.2.0", + "unzipper": "^0.10.11", + "uuid": "^8.3.0" + }, + "engines": { + "node": ">=8.3.0" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/express": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express/node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-csv": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/fast-csv/-/fast-csv-4.3.6.tgz", + "integrity": "sha512-2RNSpuwwsJGP0frGsOmTb9oUF+VkFSM4SyLTDgwf2ciHWTarN0lQTC+F2f/t5J9QjW+c65VFIAAu85GsvMIusw==", + "dependencies": { + "@fast-csv/format": "4.3.5", + "@fast-csv/parse": "4.3.6" + }, + "engines": { + "node": ">=10.0.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==" + }, + "node_modules/fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", + "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/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==" + }, + "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==" + }, + "node_modules/fast-url-parser": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz", + "integrity": "sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ==", + "dependencies": { + "punycode": "^1.3.2" + } + }, + "node_modules/fast-url-parser/node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==" + }, + "node_modules/fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/fbemitter": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/fbemitter/-/fbemitter-3.0.0.tgz", + "integrity": "sha512-KWKaceCwKQU0+HPoop6gn4eOHk50bBv/VxjJtGMfwmJt3D29JpN4H4eisCtIPA+a8GVBam+ldMMpMjJUvpDyHw==", + "dependencies": { + "fbjs": "^3.0.0" + } + }, + "node_modules/fbjs": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-3.0.5.tgz", + "integrity": "sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg==", + "dependencies": { + "cross-fetch": "^3.1.5", + "fbjs-css-vars": "^1.0.0", + "loose-envify": "^1.0.0", + "object-assign": "^4.1.0", + "promise": "^7.1.1", + "setimmediate": "^1.0.5", + "ua-parser-js": "^1.0.35" + } + }, + "node_modules/fbjs-css-vars": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz", + "integrity": "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==" + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/feed": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/feed/-/feed-4.2.2.tgz", + "integrity": "sha512-u5/sxGfiMfZNtJ3OvQpXcvotFpYkL0n9u9mM2vkui2nGo8b4wvDkJ8gAkYqbA8QpGyFCv3RK0Z+Iv+9veCS9bQ==", + "dependencies": { + "xml-js": "^1.6.11" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "dev": true + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/file-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/file-loader/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dev": true, + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/filesize": { + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz", + "integrity": "sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "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==", + "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-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true + }, + "node_modules/flux": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/flux/-/flux-4.0.4.tgz", + "integrity": "sha512-NCj3XlayA2UsapRpM7va6wU1+9rE5FIL7qoMcmxWHRzbp0yujihMBm9BBHZ1MDIk5h5o2Bl6eGiCe8rYELAmYw==", + "dependencies": { + "fbemitter": "^3.0.0", + "fbjs": "^3.0.1" + }, + "peerDependencies": { + "react": "^15.0.2 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", + "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/fork-ts-checker-webpack-plugin": { + "version": "7.2.13", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-7.2.13.tgz", + "integrity": "sha512-fR3WRkOb4bQdWB/y7ssDUlVdrclvwtyCUIHCfivAoYxq9dF7XfrDKbMdZIfwJ7hxIAqkYSGeU7lLJE6xrxIBdg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.16.7", + "chalk": "^4.1.2", + "chokidar": "^3.5.3", + "cosmiconfig": "^7.0.1", + "deepmerge": "^4.2.2", + "fs-extra": "^10.0.0", + "memfs": "^3.4.1", + "minimatch": "^3.0.4", + "node-abort-controller": "^3.0.1", + "schema-utils": "^3.1.1", + "semver": "^7.3.5", + "tapable": "^2.2.1" + }, + "engines": { + "node": ">=12.13.0", + "yarn": ">=1.0.0" + }, + "peerDependencies": { + "typescript": ">3.6.0", + "vue-template-compiler": "*", + "webpack": "^5.11.0" + }, + "peerDependenciesMeta": { + "vue-template-compiler": { + "optional": true + } + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fp-ts": { + "version": "2.16.5", + "resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-2.16.5.tgz", + "integrity": "sha512-N8T8PwMSeTKKtkm9lkj/zSTAnPC/aJIIrQhnHxxkL0KLsRCNUPANksJOlMXxcKKCo7H1ORP3No9EMD+fP0tsdA==" + }, + "node_modules/fraction.js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", + "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://www.patreon.com/infusion" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-monkey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", + "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==" + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "dependencies": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + }, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/fstream/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/fstream/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "node_modules/function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/generic-names": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/generic-names/-/generic-names-4.0.0.tgz", + "integrity": "sha512-ySFolZQfw9FoDb3ed9d80Cm9f0+r7qj+HJkWjeD9RBfpxEVTlVhol+gvaQB/78WbwYfbnNh8nWHHBSlg072y6A==", + "dev": true, + "dependencies": { + "loader-utils": "^3.2.0" + } + }, + "node_modules/generic-names/node_modules/loader-utils": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz", + "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==", + "dev": true, + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "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-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", + "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-own-enumerable-property-symbols": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", + "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==" + }, + "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-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "dev": true, + "optional": true + }, + "node_modules/github-slugger": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.5.0.tgz", + "integrity": "sha512-wIh+gKBI9Nshz2o46B0B3f5k/W+WI9ZAv6y5Dn5WJ5SK1t0TnDimB4WE5rmTD05ZAIn8HALCZVmCsvj0w0v0lw==" + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/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-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/global-dirs": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", + "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==", + "dependencies": { + "ini": "2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/global-dirs/node_modules/ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "dependencies": { + "global-prefix": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "dependencies": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "engines": { + "node": ">=6" + } + }, + "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==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/globby": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.1.tgz", + "integrity": "sha512-sSs4inE1FB2YQiymcmTv6NWENryABjUNPeWhOvmn4SjtKybglsyPZxFB3U1/+L1bYi0rNZDqCLlHyLYDl1Pq5A==", + "dev": true, + "dependencies": { + "@types/glob": "^7.1.1", + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.0.3", + "glob": "^7.1.3", + "ignore": "^5.1.1", + "merge2": "^1.2.3", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/gray-matter": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", + "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==", + "dependencies": { + "js-yaml": "^3.13.1", + "kind-of": "^6.0.2", + "section-matter": "^1.0.0", + "strip-bom-string": "^1.0.0" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/gtfs-realtime-bindings": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/gtfs-realtime-bindings/-/gtfs-realtime-bindings-1.1.1.tgz", + "integrity": "sha512-+k8+/MmiBmUUWlASs4CeTkV+Qyz/FgbZxXdg9rDU62XRfJOpRaRe+nKWCGKse965jffVZ0tIu1K+R7hRvjSLfQ==", + "dependencies": { + "protobufjs": "^7.1.2", + "protobufjs-cli": "^1.0.2" + } + }, + "node_modules/gzip-size": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", + "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", + "dependencies": { + "duplexer": "^0.1.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==" + }, + "node_modules/harmony-reflect": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz", + "integrity": "sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==", + "dev": true + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dependencies": { + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" + }, + "node_modules/has-yarn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", + "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/hast-to-hyperscript": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/hast-to-hyperscript/-/hast-to-hyperscript-9.0.1.tgz", + "integrity": "sha512-zQgLKqF+O2F72S1aa4y2ivxzSlko3MAvxkwG8ehGmNiqd98BIN3JM1rAJPmplEyLmGLO2QZYJtIneOSZ2YbJuA==", + "dependencies": { + "@types/unist": "^2.0.3", + "comma-separated-tokens": "^1.0.0", + "property-information": "^5.3.0", + "space-separated-tokens": "^1.0.0", + "style-to-object": "^0.3.0", + "unist-util-is": "^4.0.0", + "web-namespaces": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-6.0.1.tgz", + "integrity": "sha512-jeJUWiN5pSxW12Rh01smtVkZgZr33wBokLzKLwinYOUfSzm1Nl/c3GUGebDyOKjdsRgMvoVbV0VpAcpjF4NrJA==", + "dependencies": { + "@types/parse5": "^5.0.0", + "hastscript": "^6.0.0", + "property-information": "^5.0.0", + "vfile": "^4.0.0", + "vfile-location": "^3.2.0", + "web-namespaces": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-parse5/node_modules/@types/parse5": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@types/parse5/-/parse5-5.0.3.tgz", + "integrity": "sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw==" + }, + "node_modules/hast-util-parse-selector": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz", + "integrity": "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-raw": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-6.0.1.tgz", + "integrity": "sha512-ZMuiYA+UF7BXBtsTBNcLBF5HzXzkyE6MLzJnL605LKE8GJylNjGc4jjxazAHUtcwT5/CEt6afRKViYB4X66dig==", + "dependencies": { + "@types/hast": "^2.0.0", + "hast-util-from-parse5": "^6.0.0", + "hast-util-to-parse5": "^6.0.0", + "html-void-elements": "^1.0.0", + "parse5": "^6.0.0", + "unist-util-position": "^3.0.0", + "vfile": "^4.0.0", + "web-namespaces": "^1.0.0", + "xtend": "^4.0.0", + "zwitch": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-raw/node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" + }, + "node_modules/hast-util-to-parse5": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-6.0.0.tgz", + "integrity": "sha512-Lu5m6Lgm/fWuz8eWnrKezHtVY83JeRGaNQ2kn9aJgqaxvVkFCZQBEhgodZUDUvoodgyROHDb3r5IxAEdl6suJQ==", + "dependencies": { + "hast-to-hyperscript": "^9.0.0", + "property-information": "^5.0.0", + "web-namespaces": "^1.0.0", + "xtend": "^4.0.0", + "zwitch": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hastscript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz", + "integrity": "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==", + "dependencies": { + "@types/hast": "^2.0.0", + "comma-separated-tokens": "^1.0.0", + "hast-util-parse-selector": "^2.0.0", + "property-information": "^5.0.0", + "space-separated-tokens": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "bin": { + "he": "bin/he" + } + }, + "node_modules/heap": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.7.tgz", + "integrity": "sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==" + }, + "node_modules/history": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", + "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==", + "dependencies": { + "@babel/runtime": "^7.1.2", + "loose-envify": "^1.2.0", + "resolve-pathname": "^3.0.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0", + "value-equal": "^1.0.1" + } + }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/hoist-non-react-statics/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", + "dependencies": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "node_modules/hpack.js/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/hpack.js/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/hpack.js/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "dev": true, + "dependencies": { + "whatwg-encoding": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/html-entities": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", + "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==" + }, + "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/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", + "dependencies": { + "camel-case": "^4.1.2", + "clean-css": "^5.2.2", + "commander": "^8.3.0", + "he": "^1.2.0", + "param-case": "^3.0.4", + "relateurl": "^0.2.7", + "terser": "^5.10.0" + }, + "bin": { + "html-minifier-terser": "cli.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/html-tags": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.2.0.tgz", + "integrity": "sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/html-void-elements": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-1.0.5.tgz", + "integrity": "sha512-uE/TxKuyNIcx44cIWnjr/rfIATDH7ZaOMmstu0CwhFG1Dunhlp4OC6/NMbhiwoq5BpW0ubi303qnEk/PZj614w==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/html-webpack-plugin": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz", + "integrity": "sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==", + "dependencies": { + "@types/html-minifier-terser": "^6.0.0", + "html-minifier-terser": "^6.0.2", + "lodash": "^4.17.21", + "pretty-error": "^4.0.0", + "tapable": "^2.0.0" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/html-webpack-plugin" + }, + "peerDependencies": { + "webpack": "^5.20.0" + } + }, + "node_modules/htmlparser2": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.1.tgz", + "integrity": "sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "entities": "^4.3.0" + } + }, + "node_modules/htmlparser2/node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/htmlparser2/node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/htmlparser2/node_modules/domutils": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz", + "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.1" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", + "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==" + }, + "node_modules/http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==" + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==" + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-proxy-middleware": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", + "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "dependencies": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@types/express": "^4.17.13" + }, + "peerDependenciesMeta": { + "@types/express": { + "optional": true + } + } + }, + "node_modules/http-server": { + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/http-server/-/http-server-14.1.1.tgz", + "integrity": "sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A==", + "dev": true, + "dependencies": { + "basic-auth": "^2.0.1", + "chalk": "^4.1.2", + "corser": "^2.0.1", + "he": "^1.2.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy": "^1.18.1", + "mime": "^1.6.0", + "minimist": "^1.2.6", + "opener": "^1.5.1", + "portfinder": "^1.0.28", + "secure-compare": "3.0.1", + "union": "~0.5.0", + "url-join": "^4.0.1" + }, + "bin": { + "http-server": "bin/http-server" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "optional": true, + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/icss-replace-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", + "integrity": "sha512-chIaY3Vh2mh2Q3RGXttaDIzeiPvaVXJ+C4DAh/w3c37SKZ/U6PGMmuicR2EQQp9bKG8zLMCl7I+PtIoOOPp8Gg==", + "dev": true + }, + "node_modules/icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/identity-obj-proxy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz", + "integrity": "sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==", + "dev": true, + "dependencies": { + "harmony-reflect": "^1.4.6" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ignore": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", + "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==", + "dev": true, + "optional": true, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" + }, + "node_modules/immer": { + "version": "9.0.19", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.19.tgz", + "integrity": "sha512-eY+Y0qcsB4TZKwgQzLaE/lqYMlKhv5J9dyd2RhhtGhNo2njPXDqU9XPfcNfa3MIDsdtZt5KlkIsirlo4dHsWdQ==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/immutable": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.5.tgz", + "integrity": "sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw==", + "dev": true + }, + "node_modules/import-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-3.0.0.tgz", + "integrity": "sha512-4pnzH16plW+hgvRECbDWpQl3cqtvSofHWh44met7ESfZ8UZOWWddm8hEyDTqREJ9RbYHY8gi8DqmaelApoOGMg==", + "dev": true, + "dependencies": { + "import-from": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "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==", + "engines": { + "node": ">=4" + } + }, + "node_modules/import-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-3.0.0.tgz", + "integrity": "sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-lazy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", + "integrity": "sha512-m7ZEHgtw69qOGw+jwxXkHlrlIPdTGkyh66zXZ1ajZbxkDBNjSY/LGbmjc7h0s2ELsUDTAhFr55TrPSSqJGPG0A==", + "engines": { + "node": ">=4" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "optional": true + }, + "node_modules/infima": { + "version": "0.2.0-alpha.43", + "resolved": "https://registry.npmjs.org/infima/-/infima-0.2.0-alpha.43.tgz", + "integrity": "sha512-2uw57LvUqW0rK/SWYnd/2rRfxNA5DDNOh33jxF7fy46VWoNhGxiUQyVZHbBMjQ33mQem0cjdDVwgWVAmlRfgyQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, + "node_modules/inline-style-parser": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", + "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==" + }, + "node_modules/internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/ip": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", + "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==", + "optional": true + }, + "node_modules/ipaddr.js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", + "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/is-alphabetical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", + "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", + "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", + "dependencies": { + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-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==", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-builtin-module": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", + "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", + "dev": true, + "dependencies": { + "builtin-modules": "^3.3.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dependencies": { + "ci-info": "^2.0.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/is-ci/node_modules/ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" + }, + "node_modules/is-core-module": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-decimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", + "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "engines": { + "node": ">=0.10.0" + } + }, + "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": "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/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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-hexadecimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", + "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-installed-globally": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", + "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", + "dependencies": { + "global-dirs": "^3.0.0", + "is-path-inside": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", + "optional": true + }, + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", + "dev": true + }, + "node_modules/is-nan": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", + "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-npm": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-5.0.0.tgz", + "integrity": "sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==", + "engines": { + "node": ">=10" + }, + "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-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true + }, + "node_modules/is-reference": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", + "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", + "dev": true, + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-root": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz", + "integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-what": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", + "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==", + "dev": true + }, + "node_modules/is-whitespace-character": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz", + "integrity": "sha512-SDweEzfIZM0SJV0EUga669UTKlmL0Pq8Lno0QDQsPnvECB3IM2aP0gdx5TrU0A01MAPfViaZiI2V1QMZLaKK5w==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-word-character": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-word-character/-/is-word-character-1.0.4.tgz", + "integrity": "sha512-5SMO8RVennx3nZrqtKwCGyyetPE9VDba5ugvKLaD4KopPG5kR4mQ7tNt/r7feL5yt5h3lpuBbIUmCOG2eSzXHA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-yarn-global": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", + "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==" + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "engines": { + "node": ">=0.10.0" + } + }, + "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-instrument": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.1.tgz", + "integrity": "sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/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.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", + "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jake": { + "version": "10.8.5", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz", + "integrity": "sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==", + "dev": true, + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.1", + "minimatch": "^3.0.4" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "dev": true, + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-jsdom": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz", + "integrity": "sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/jsdom": "^20.0.0", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0", + "jsdom": "^20.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve/node_modules/resolve.exports": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner/node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/joi": { + "version": "17.7.1", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.7.1.tgz", + "integrity": "sha512-teoLhIvWE298R6AeJywcjR4sX2hHjB3/xJX4qPjg+gTg+c0mzUDsziYlqPmLomq9gVsfaMcgPaGc7VxtD/9StA==", + "dependencies": { + "@hapi/hoek": "^9.0.0", + "@hapi/topo": "^5.0.0", + "@sideway/address": "^4.1.3", + "@sideway/formula": "^3.0.1", + "@sideway/pinpoint": "^2.0.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "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==", + "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==", + "dependencies": { + "xmlcreate": "^2.0.4" + } + }, + "node_modules/jsdoc": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.2.tgz", + "integrity": "sha512-e8cIg2z62InH7azBBi3EsSEqrKx+nUtAS5bBcYTSpZFA+vhNPyhv8PTFZ0WsjOPDj04/dOLlm08EDcQJDqaGQg==", + "dependencies": { + "@babel/parser": "^7.20.15", + "@jsdoc/salty": "^0.2.1", + "@types/markdown-it": "^12.2.3", + "bluebird": "^3.7.2", + "catharsis": "^0.9.0", + "escape-string-regexp": "^2.0.0", + "js2xmlparser": "^4.0.2", + "klaw": "^3.0.0", + "markdown-it": "^12.3.2", + "markdown-it-anchor": "^8.4.1", + "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/node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, + "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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jsdom": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", + "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==", + "dev": true, + "dependencies": { + "abab": "^2.0.6", + "acorn": "^8.8.1", + "acorn-globals": "^7.0.0", + "cssom": "^0.5.0", + "cssstyle": "^2.3.0", + "data-urls": "^3.0.2", + "decimal.js": "^10.4.2", + "domexception": "^4.0.0", + "escodegen": "^2.0.0", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.1", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.2", + "parse5": "^7.1.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.1.2", + "w3c-xmlserializer": "^4.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^2.0.0", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0", + "ws": "^8.11.0", + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsdom/node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "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==" + }, + "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==" + }, + "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==" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-eslint-parser": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/jsonc-eslint-parser/-/jsonc-eslint-parser-2.4.0.tgz", + "integrity": "sha512-WYDyuc/uFcGp6YtM2H0uKmUwieOuzeE/5YocFJLnLfclZ4inf3mRn8ZVy1s7Hxji7Jxm6Ss8gqpexD/GlKoGgg==", + "dev": true, + "dependencies": { + "acorn": "^8.5.0", + "eslint-visitor-keys": "^3.0.0", + "espree": "^9.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ota-meshi" + } + }, + "node_modules/jsonc-eslint-parser/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jsonc-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", + "dev": true + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonschema": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.4.1.tgz", + "integrity": "sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz", + "integrity": "sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.5", + "object.assign": "^4.1.3" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/jszip/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/jszip/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/jszip/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/keytar": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/keytar/-/keytar-7.9.0.tgz", + "integrity": "sha512-VPD8mtVtm5JNtA2AErl6Chp06JBfy7diFQ7TQQhdpWOl6MrCRB+eRbvAZUsbGQS9kiMq0coJsy0W0vHpDCkWsQ==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "dependencies": { + "node-addon-api": "^4.3.0", + "prebuild-install": "^7.0.1" + } + }, + "node_modules/keytar/node_modules/node-addon-api": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", + "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==", + "dev": true, + "optional": true + }, + "node_modules/keyv": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.2.tgz", + "integrity": "sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g==", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/khroma": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/khroma/-/khroma-2.0.0.tgz", + "integrity": "sha512-2J8rDNlQWbtiNYThZRvmMv5yt44ZakX+Tz5ZIp/mN1pt4snn+m030Va5Z4v8xA0cQFDXBwO/8i42xL4QPsVk3g==" + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/klaw": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", + "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", + "dependencies": { + "graceful-fs": "^4.1.9" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "engines": { + "node": ">=6" + } + }, + "node_modules/klona": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", + "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/langium": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/langium/-/langium-3.0.0.tgz", + "integrity": "sha512-+Ez9EoiByeoTu/2BXmEaZ06iPNXM6thWJp02KfBO/raSMyCJ4jw7AkWWa+zBCTm0+Tw1Fj9FOxdqSskyN5nAwg==", + "dependencies": { + "chevrotain": "~11.0.3", + "chevrotain-allstar": "~0.3.0", + "vscode-languageserver": "~9.0.1", + "vscode-languageserver-textdocument": "~1.0.11", + "vscode-uri": "~3.0.8" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/langium-cli": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/langium-cli/-/langium-cli-3.0.3.tgz", + "integrity": "sha512-g6PdhEq5IiYWK/oiySILglPvFdK6ofQdzC+U7PJmFH++bDKu0DGdxjWzDauUN5WUDyVQETWKgtYDmmbcxPzN0w==", + "dev": true, + "dependencies": { + "chalk": "~5.3.0", + "commander": "~11.0.0", + "fs-extra": "~11.1.1", + "jsonschema": "~1.4.1", + "langium": "~3.0.0", + "langium-railroad": "~3.0.0", + "lodash": "~4.17.21" + }, + "bin": { + "langium": "bin/langium.js" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/langium-cli/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/langium-cli/node_modules/commander": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.0.0.tgz", + "integrity": "sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==", + "dev": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/langium-cli/node_modules/fs-extra": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", + "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/langium-railroad": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/langium-railroad/-/langium-railroad-3.0.0.tgz", + "integrity": "sha512-GQOnQBGl5gJqzgK/4bKvJO5QhJGNnprpYH6Fghbl4FviVLHwP6yzyqiouDelLSoCadChCr2JqKaBp5HXv7CgWw==", + "dev": true, + "dependencies": { + "langium": "~3.0.0", + "railroad-diagrams": "~1.0.0" + } + }, + "node_modules/language-subtag-registry": { + "version": "0.3.22", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", + "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==", + "dev": true + }, + "node_modules/language-tags": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.6.tgz", + "integrity": "sha512-HNkaCgM8wZgE/BZACeotAAgpL9FUjEnhgF0FVQMIgH//zqTPreLYMb3rWYkYAqPoF75Jwuycp1da7uz66cfFQg==", + "dev": true, + "dependencies": { + "language-subtag-registry": "^0.3.20" + } + }, + "node_modules/latest-version": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", + "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", + "dependencies": { + "package-json": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/layout-base": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz", + "integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==" + }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/lazystream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/lazystream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/lazystream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/less": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/less/-/less-4.1.3.tgz", + "integrity": "sha512-w16Xk/Ta9Hhyei0Gpz9m7VS8F28nieJaL/VyShID7cYvP6IL5oHeL6p4TXSDJqZE/lNv0oJ2pGVjJsRkfwm5FA==", + "dev": true, + "dependencies": { + "copy-anything": "^2.0.1", + "parse-node-version": "^1.0.1", + "tslib": "^2.3.0" + }, + "bin": { + "lessc": "bin/lessc" + }, + "engines": { + "node": ">=6" + }, + "optionalDependencies": { + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "make-dir": "^2.1.0", + "mime": "^1.4.1", + "needle": "^3.1.0", + "source-map": "~0.6.0" + } + }, + "node_modules/less-loader": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-11.1.0.tgz", + "integrity": "sha512-C+uDBV7kS7W5fJlUjq5mPBeBVhYpTIm5gB09APT9o3n/ILeaXVsiSFTbZpTJCJwQ/Crczfn3DmfQFwxYusWFug==", + "dev": true, + "dependencies": { + "klona": "^2.0.4" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "less": "^3.5.0 || ^4.0.0", + "webpack": "^5.0.0" + } + }, + "node_modules/less/node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "optional": true, + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/less/node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/less/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "optional": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/license-webpack-plugin": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-4.0.2.tgz", + "integrity": "sha512-771TFWFD70G1wLTC4oU2Cw4qvtmNrIw+wRvBtn+okgHl7slJVi7zfNcdmqDL72BojM30VNJ2UHylr1o77U37Jw==", + "dev": true, + "dependencies": { + "webpack-sources": "^3.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + }, + "webpack-sources": { + "optional": true + } + } + }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/lilconfig": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", + "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==", + "engines": { + "node": ">=10" + } + }, + "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==" + }, + "node_modules/linkify-it": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", + "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", + "dependencies": { + "uc.micro": "^1.0.1" + } + }, + "node_modules/listenercount": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", + "integrity": "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==" + }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/local-pkg": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.0.tgz", + "integrity": "sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==", + "dev": true, + "dependencies": { + "mlly": "^1.4.2", + "pkg-types": "^1.0.3" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "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==", + "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==" + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "dev": true + }, + "node_modules/lodash.curry": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.curry/-/lodash.curry-4.1.1.tgz", + "integrity": "sha512-/u14pXGviLaweY5JI0IUzgzF2J6Ne8INyzAZjImcryjgkZ+ebruBxy2/JaOOkTqScddcYtakjhSaeemV8lR0tA==" + }, + "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==" + }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" + }, + "node_modules/lodash.difference": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", + "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==" + }, + "node_modules/lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==" + }, + "node_modules/lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==" + }, + "node_modules/lodash.flow": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/lodash.flow/-/lodash.flow-3.5.0.tgz", + "integrity": "sha512-ff3BX/tSioo+XojX4MOsOMhJw0nZoUEF011LX8g8d3gvjVbxd89cCio4BCXronjxcTUIJUoqKEUA+n4CqvvRPw==" + }, + "node_modules/lodash.groupby": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.groupby/-/lodash.groupby-4.6.0.tgz", + "integrity": "sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" + }, + "node_modules/lodash.isfunction": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz", + "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==" + }, + "node_modules/lodash.isnil": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/lodash.isnil/-/lodash.isnil-4.0.0.tgz", + "integrity": "sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng==" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "node_modules/lodash.isundefined": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz", + "integrity": "sha512-MXB1is3s899/cD8jheYYE2V9qTHwKvt+npCwpD+1Sxm3Q3cECXCiYHjeHWXNwr6Q0SOBPrYUDxendrO6goVTEA==" + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==" + }, + "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.union": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", + "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==" + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/long": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.1.tgz", + "integrity": "sha512-GKSNGeNAtw8IryjjkhZxuKB3JzlcLTwjtiQCHKvqQet81I93kXslhDQruGI/QsddO83mcDToBVy7GqGS/zYf/A==" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.1" + } + }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/magic-string": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", + "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", + "dev": true, + "dependencies": { + "sourcemap-codec": "^1.4.8" + } + }, + "node_modules/magicast": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.4.tgz", + "integrity": "sha512-TyDF/Pn36bBji9rWKHlZe+PZb6Mx5V8IHCSxk7X4aljM4e/vyDvZZYwHewdVaqiA0nb3ghfHU/6AUpDxWoER2Q==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.24.4", + "@babel/types": "^7.24.0", + "source-map-js": "^1.2.0" + } + }, + "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==", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/make-fetch-happen": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", + "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", + "optional": true, + "dependencies": { + "agentkeepalive": "^4.1.3", + "cacache": "^15.2.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.2", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^6.0.0", + "ssri": "^8.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/make-fetch-happen/node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "optional": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/make-fetch-happen/node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "optional": true, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/markdown-escapes": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/markdown-escapes/-/markdown-escapes-1.0.4.tgz", + "integrity": "sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/markdown-it": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", + "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", + "dependencies": { + "argparse": "^2.0.1", + "entities": "~2.1.0", + "linkify-it": "^3.0.1", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + }, + "bin": { + "markdown-it": "bin/markdown-it.js" + } + }, + "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==", + "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==" + }, + "node_modules/markdown-it/node_modules/entities": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", + "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/marked": { + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.2.12.tgz", + "integrity": "sha512-yr8hSKa3Fv4D3jdZmtMMPghgVt6TWbk86WQaWhDloQjRSQhMMYCAro7jP7VDJrjjdV8pxVxMssXS8B8Y5DZ5aw==", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/mdast-squeeze-paragraphs": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-squeeze-paragraphs/-/mdast-squeeze-paragraphs-4.0.0.tgz", + "integrity": "sha512-zxdPn69hkQ1rm4J+2Cs2j6wDEv7O17TfXTJ33tl/+JPIoEmtV9t2ZzBM5LPHE8QlHsmVD8t3vPKCyY3oH+H8MQ==", + "dependencies": { + "unist-util-remove": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-definitions": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-4.0.0.tgz", + "integrity": "sha512-k8AJ6aNnUkB7IE+5azR9h81O5EQ/cTDXtWdMq9Kk5KcEW/8ritU5CeLg/9HhOC++nALHBlaogJ5jz0Ybk3kPMQ==", + "dependencies": { + "unist-util-visit": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-10.0.1.tgz", + "integrity": "sha512-BW3LM9SEMnjf4HXXVApZMt8gLQWVNXc3jryK0nJu/rOXPOnlkUjmdkDlmxMirpbU9ILncGFIwLH/ubnWBbcdgA==", + "dependencies": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "mdast-util-definitions": "^4.0.0", + "mdurl": "^1.0.0", + "unist-builder": "^2.0.0", + "unist-util-generated": "^1.0.0", + "unist-util-position": "^3.0.0", + "unist-util-visit": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz", + "integrity": "sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" + }, + "node_modules/mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==" + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memfs": { + "version": "3.4.12", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.12.tgz", + "integrity": "sha512-BcjuQn6vfqP+k100e0E9m61Hyqa//Brp+I3f0OBmN0ATHlFA8vx3Lt8z57R3u2bPqe3WGDBC+nF72fTH7isyEw==", + "dependencies": { + "fs-monkey": "^1.0.3" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, + "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/mermaid": { + "version": "9.4.3", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-9.4.3.tgz", + "integrity": "sha512-TLkQEtqhRSuEHSE34lh5bCa94KATCyluAXmFnNI2PRZwOpXFeqiJWwZl+d2CcemE1RS6QbbueSSq9QIg8Uxcyw==", + "dependencies": { + "@braintree/sanitize-url": "^6.0.0", + "cytoscape": "^3.23.0", + "cytoscape-cose-bilkent": "^4.1.0", + "cytoscape-fcose": "^2.1.0", + "d3": "^7.4.0", + "dagre-d3-es": "7.0.9", + "dayjs": "^1.11.7", + "dompurify": "2.4.3", + "elkjs": "^0.8.2", + "khroma": "^2.0.0", + "lodash-es": "^4.17.21", + "non-layered-tidy-tree-layout": "^2.0.2", + "stylis": "^4.1.2", + "ts-dedent": "^2.2.0", + "uuid": "^9.0.0", + "web-worker": "^1.2.0" + } + }, + "node_modules/mermaid/node_modules/uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/mini-css-extract-plugin": { + "version": "2.4.7", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.4.7.tgz", + "integrity": "sha512-euWmddf0sk9Nv1O0gfeeUAvAkoSlWncNLF77C0TP2+WoPvy8mAHKOzMajcCz2dzvyt3CNgxb1obIEVFIRxaipg==", + "dev": true, + "dependencies": { + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/mini-css-extract-plugin/node_modules/schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/mini-svg-data-uri": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz", + "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==", + "dev": true, + "bin": { + "mini-svg-data-uri": "cli.js" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "node_modules/minimatch": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.5.tgz", + "integrity": "sha512-tUpxzX0VAzJHjLu0xUfFv1gwVp9ba3IOuRAVH2EGuRW8a5emA2FlACLqiT/lDVtS1W+TGNwqz3sWaNyLgDJWuw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-fetch": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", + "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", + "optional": true, + "dependencies": { + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "optionalDependencies": { + "encoding": "^0.1.12" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true, + "optional": true + }, + "node_modules/mlly": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.0.tgz", + "integrity": "sha512-U9SDaXGEREBYQgfejV97coK0UL1r+qnF2SyO9A3qcI8MzKnsIFKHNVEkrDyNncQTKQQumsasmeq84eNMdBfsNQ==", + "dev": true, + "dependencies": { + "acorn": "^8.11.3", + "pathe": "^1.1.2", + "pkg-types": "^1.1.0", + "ufo": "^1.5.3" + } + }, + "node_modules/monaco-editor": { + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.34.1.tgz", + "integrity": "sha512-FKc80TyiMaruhJKKPz5SpJPIjL+dflGvz4CpuThaPMc94AyN7SeC9HQ8hrvaxX7EyHdJcUY5i4D0gNyJj1vSZQ==" + }, + "node_modules/monaco-languageclient": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/monaco-languageclient/-/monaco-languageclient-4.0.3.tgz", + "integrity": "sha512-1mGIUb5PFRknITBhNxgH0SnQy1/jntt9oo0cQpOl3HdhYEL/CYK2UrsZZX7Udqmz1PXKyRIzQ3tZ7dJn4mzWtA==", + "dependencies": { + "vscode": "npm:@codingame/monaco-vscode-api@1.69.13", + "vscode-jsonrpc": "8.0.2", + "vscode-languageclient": "8.0.2" + }, + "engines": { + "node": ">=16.11.0", + "npm": ">=8.0.0" + }, + "peerDependencies": { + "vscode": ">= npm:@codingame/monaco-vscode-api@1.69.0 < npm:@codingame/monaco-vscode-api@1.70.0" + } + }, + "node_modules/monaco-languageclient/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/monaco-languageclient/node_modules/vscode-languageclient": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-8.0.2.tgz", + "integrity": "sha512-lHlthJtphG9gibGb/y72CKqQUxwPsMXijJVpHEC2bvbFqxmkj9LwQ3aGU9dwjBLqsX1S4KjShYppLvg1UJDF/Q==", + "dependencies": { + "minimatch": "^3.0.4", + "semver": "^7.3.5", + "vscode-languageserver-protocol": "3.17.2" + }, + "engines": { + "vscode": "^1.67.0" + } + }, + "node_modules/mrmime": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz", + "integrity": "sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/multicast-dns": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", + "dependencies": { + "dns-packet": "^5.2.2", + "thunky": "^1.0.2" + }, + "bin": { + "multicast-dns": "cli.js" + } + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "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/napi-build-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", + "dev": true, + "optional": true + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true + }, + "node_modules/needle": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/needle/-/needle-3.3.1.tgz", + "integrity": "sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==", + "dev": true, + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.3", + "sax": "^1.2.4" + }, + "bin": { + "needle": "bin/needle" + }, + "engines": { + "node": ">= 4.4.x" + } + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/nock": { + "version": "13.3.1", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.3.1.tgz", + "integrity": "sha512-vHnopocZuI93p2ccivFyGuUfzjq2fxNyNurp7816mlT5V5HF4SzXu8lvLrVzBbNqzs+ODooZ6OksuSUNM7Njkw==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "json-stringify-safe": "^5.0.1", + "lodash": "^4.17.21", + "propagate": "^2.0.0" + }, + "engines": { + "node": ">= 10.13" + } + }, + "node_modules/node-abi": { + "version": "3.33.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.33.0.tgz", + "integrity": "sha512-7GGVawqyHF4pfd0YFybhv/eM9JwTtPqx0mAanQ146O3FlSh3pA24zf9IRQTOsfTSqXTNzPSP5iagAJ94jjuVog==", + "dev": true, + "optional": true, + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-abi/node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "optional": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-abort-controller": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", + "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==", + "dev": true + }, + "node_modules/node-emoji": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", + "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", + "dependencies": { + "lodash": "^4.17.21" + } + }, + "node_modules/node-fetch": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz", + "integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/node-gyp": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", + "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", + "optional": true, + "dependencies": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^9.1.0", + "nopt": "^5.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": ">= 10.12.0" + } + }, + "node_modules/node-gyp/node_modules/are-we-there-yet": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", + "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", + "optional": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/node-gyp/node_modules/gauge": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "optional": true, + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/node-gyp/node_modules/npmlog": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", + "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "optional": true, + "dependencies": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/node-gyp/node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "optional": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true + }, + "node_modules/node-machine-id": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/node-machine-id/-/node-machine-id-1.1.12.tgz", + "integrity": "sha512-QNABxbrPa3qEIfrE6GOJ7BYIuignnJw7iQ2YPbc3Nla1HzRJjXzZOiikfF8m7eAMfichLt3M4VgLOetqgDmgGQ==", + "dev": true + }, + "node_modules/node-releases": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==" + }, + "node_modules/nodejs-polars": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/nodejs-polars/-/nodejs-polars-0.11.0.tgz", + "integrity": "sha512-rLhYkhcMCGrRXSUOVt+wskNiqaSapEWpo7hPlhQvEFkjcn4NPO/0RPHswGezOdIgQ3h778jpNkIXQjxdD5bXdA==", + "workspaces": [ + "benches" + ], + "engines": { + "node": ">= 18" + }, + "optionalDependencies": { + "nodejs-polars-android-arm64": "0.11.0", + "nodejs-polars-darwin-arm64": "0.11.0", + "nodejs-polars-darwin-x64": "0.11.0", + "nodejs-polars-linux-arm64-gnu": "0.11.0", + "nodejs-polars-linux-arm64-musl": "0.11.0", + "nodejs-polars-linux-x64-gnu": "0.11.0", + "nodejs-polars-linux-x64-musl": "0.11.0", + "nodejs-polars-win32-x64-msvc": "0.11.0" + } + }, + "node_modules/nodejs-polars-android-arm64": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/nodejs-polars-android-arm64/-/nodejs-polars-android-arm64-0.11.0.tgz", + "integrity": "sha512-AuwmFvhrOELkfqmhV2ZArxV66XLlHNP/1vZqUgbPqG4nmvKi9J4RGRnKXNW+BjDgNzV/PF2OoJekEwe/Nh+whA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 16" + } + }, + "node_modules/nodejs-polars-darwin-arm64": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/nodejs-polars-darwin-arm64/-/nodejs-polars-darwin-arm64-0.11.0.tgz", + "integrity": "sha512-4VuRbWV0s5KlP8Zdw0GY2SGRXS25Ih/3qo0F3VVObDDxu60sU0m32z3fGMziphQw0Dl7Ldk1cloYyu72gnfKCQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 16" + } + }, + "node_modules/nodejs-polars-darwin-x64": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/nodejs-polars-darwin-x64/-/nodejs-polars-darwin-x64-0.11.0.tgz", + "integrity": "sha512-SAjq47Mk1cSBpf0czTxSbHMM5vTCwKdnsSFQTtUYyjqN+22l8h5oTDPOppMQ6coq0DxLugnSvmrIOeZ0HY213A==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 16" + } + }, + "node_modules/nodejs-polars-linux-arm64-gnu": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/nodejs-polars-linux-arm64-gnu/-/nodejs-polars-linux-arm64-gnu-0.11.0.tgz", + "integrity": "sha512-BH9ePSHYDlG5ggg/YM/kvLuv8TbctRAUUEhCnpgaJeBPnrtRJC+hzbWKpTcGc+pOwvunfGS+/FG9BzLtFb9POg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 16" + } + }, + "node_modules/nodejs-polars-linux-arm64-musl": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/nodejs-polars-linux-arm64-musl/-/nodejs-polars-linux-arm64-musl-0.11.0.tgz", + "integrity": "sha512-IYZ/xIACiedX4gjklyKzD1/gOOOSvCeINEB3Vc6Ene0cQI52PZtJNwfIo44ofM3Jqx0YDrLW88y5Myu+NdwmOw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 16" + } + }, + "node_modules/nodejs-polars-linux-x64-gnu": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/nodejs-polars-linux-x64-gnu/-/nodejs-polars-linux-x64-gnu-0.11.0.tgz", + "integrity": "sha512-SnJdhVAGPAxQ5ZB3QVacCF6SnzWI7/2qp1X4AEvaVFoAaZp3ahdE+1K9qYS3EZZM5y1DdVbok1cJzQs8UlM9VA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 16" + } + }, + "node_modules/nodejs-polars-linux-x64-musl": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/nodejs-polars-linux-x64-musl/-/nodejs-polars-linux-x64-musl-0.11.0.tgz", + "integrity": "sha512-ug8f34akvrrHDU8UvObU45VswwVJYN85AUF+K5vZGh7zGUoZlwqG4PfWr2vKjSz41XrfgqkeMwoFZlTCrrMNoA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 16" + } + }, + "node_modules/nodejs-polars-win32-x64-msvc": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/nodejs-polars-win32-x64-msvc/-/nodejs-polars-win32-x64-msvc-0.11.0.tgz", + "integrity": "sha512-MF3rVxabCAy6GJ+ohaeCsGUn3pjOlHwjj0iCgjHxdJDvmXpNl2TTFqGMijicDqsRbv/TKt90o6QUrBZl94MQJQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 16" + } + }, + "node_modules/non-layered-tidy-tree-layout": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/non-layered-tidy-tree-layout/-/non-layered-tidy-tree-layout-2.0.2.tgz", + "integrity": "sha512-gkXMxRzUH+PB0ax9dUN0yYF0S25BqeAYqhgMaLUFmpXLEk7Fcu8f4emJuOAY0V8kjDICxROIKsTAKsV/v355xw==" + }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/normalize-package-data/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-package-arg": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-11.0.1.tgz", + "integrity": "sha512-M7s1BD4NxdAvBKUPqqRW957Xwcl/4Zvo8Aj+ANrzvIPzGJZElrH7Z//rSaec2ORcND6FHHLnZeY8qgTpXDMFQQ==", + "dev": true, + "dependencies": { + "hosted-git-info": "^7.0.0", + "proc-log": "^3.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^5.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm-package-arg/node_modules/hosted-git-info": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.1.tgz", + "integrity": "sha512-+K84LB1DYwMHoHSgaOY/Jfhw3ucPmSET5v98Ke/HdNSw4a0UktWzyW1mjhjpuxxTqOOsfWT/7iVshHmVZ4IpOA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm-package-arg/node_modules/hosted-git-info/node_modules/lru-cache": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", + "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", + "dev": true, + "engines": { + "node": "14 || >=16.14" + } + }, + "node_modules/npm-package-arg/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, + "node_modules/nprogress": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz", + "integrity": "sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==" + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/nwsapi": { + "version": "2.2.9", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.9.tgz", + "integrity": "sha512-2f3F0SEEer8bBu0dsNCFF50N0cTThV1nWFYcEYFZttdW0lDAoybv9cQoK7X7/68Z89S7FoRrVjP1LPX4XRf9vg==", + "dev": true + }, + "node_modules/nx": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/nx/-/nx-18.0.1.tgz", + "integrity": "sha512-AtcM7JmBC82O17WMxuu9JJxEKTcsMII1AMgxCeiCWcW22wHd3EhIn5Hg1iSFv9ftkSSd8YgHeqTciRbdTqbxpA==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@nrwl/tao": "18.0.1", + "@yarnpkg/lockfile": "^1.1.0", + "@yarnpkg/parsers": "3.0.0-rc.46", + "@zkochan/js-yaml": "0.0.6", + "axios": "^1.6.0", + "chalk": "^4.1.0", + "cli-cursor": "3.1.0", + "cli-spinners": "2.6.1", + "cliui": "^8.0.1", + "dotenv": "~16.3.1", + "dotenv-expand": "~10.0.0", + "enquirer": "~2.3.6", + "figures": "3.2.0", + "flat": "^5.0.2", + "fs-extra": "^11.1.0", + "ignore": "^5.0.4", + "jest-diff": "^29.4.1", + "js-yaml": "4.1.0", + "jsonc-parser": "3.2.0", + "lines-and-columns": "~2.0.3", + "minimatch": "9.0.3", + "node-machine-id": "1.1.12", + "npm-run-path": "^4.0.1", + "open": "^8.4.0", + "ora": "5.3.0", + "semver": "^7.5.3", + "string-width": "^4.2.3", + "strong-log-transformer": "^2.1.0", + "tar-stream": "~2.2.0", + "tmp": "~0.2.1", + "tsconfig-paths": "^4.1.2", + "tslib": "^2.3.0", + "yargs": "^17.6.2", + "yargs-parser": "21.1.1" + }, + "bin": { + "nx": "bin/nx.js", + "nx-cloud": "bin/nx-cloud.js" + }, + "optionalDependencies": { + "@nx/nx-darwin-arm64": "18.0.1", + "@nx/nx-darwin-x64": "18.0.1", + "@nx/nx-freebsd-x64": "18.0.1", + "@nx/nx-linux-arm-gnueabihf": "18.0.1", + "@nx/nx-linux-arm64-gnu": "18.0.1", + "@nx/nx-linux-arm64-musl": "18.0.1", + "@nx/nx-linux-x64-gnu": "18.0.1", + "@nx/nx-linux-x64-musl": "18.0.1", + "@nx/nx-win32-arm64-msvc": "18.0.1", + "@nx/nx-win32-x64-msvc": "18.0.1" + }, + "peerDependencies": { + "@swc-node/register": "^1.6.7", + "@swc/core": "^1.3.85" + }, + "peerDependenciesMeta": { + "@swc-node/register": { + "optional": true + }, + "@swc/core": { + "optional": true + } + } + }, + "node_modules/nx/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/nx/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/nx/node_modules/fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/nx/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/nx/node_modules/lines-and-columns": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-2.0.4.tgz", + "integrity": "sha512-wM1+Z03eypVAVUCE7QdSqpVIvelbOakn1M0bPDoA4SGWPx3sNDVUiMo3L6To6WWGClB7VyXnhQ4Sn7gxiJbE6A==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/nx/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/nx/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/nx/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/nx/node_modules/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "dependencies": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-is": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.6.tgz", + "integrity": "sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.6.tgz", + "integrity": "sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.hasown": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.2.tgz", + "integrity": "sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.values": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", + "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/opener": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", + "bin": { + "opener": "bin/opener-bin.js" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ora": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.3.0.tgz", + "integrity": "sha512-zAKMgGXUim0Jyd6CXK9lraBnD3H5yPGBPPOkC23a2BG6hsm4Zu6OQSjQuEtV0BHDf4aKHcUFvJiGRrFuW3MG8g==", + "dev": true, + "dependencies": { + "bl": "^4.0.3", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "log-symbols": "^4.0.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "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==", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-locate/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==", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-queue": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", + "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", + "dev": true, + "dependencies": { + "eventemitter3": "^4.0.4", + "p-timeout": "^3.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "dependencies": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "dev": true, + "dependencies": { + "p-finally": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", + "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", + "dependencies": { + "got": "^9.6.0", + "registry-auth-token": "^4.0.0", + "registry-url": "^5.0.0", + "semver": "^6.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/package-json/node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/package-json/node_modules/@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/package-json/node_modules/cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "engines": { + "node": ">=10.6.0" + } + }, + "node_modules/package-json/node_modules/cacheable-request": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", + "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/package-json/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json/node_modules/got": { + "version": "11.8.6", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", + "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", + "dependencies": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/package-json/node_modules/http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/package-json/node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/package-json/node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json/node_modules/p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/package-json/node_modules/responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "dependencies": { + "lowercase-keys": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/packet-reader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", + "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" + }, + "node_modules/param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "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==", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-entities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", + "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", + "dependencies": { + "character-entities": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "character-reference-invalid": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-hexadecimal": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "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==", + "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-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/parse-numeric-range": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/parse-numeric-range/-/parse-numeric-range-1.3.0.tgz", + "integrity": "sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ==" + }, + "node_modules/parse-semver": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/parse-semver/-/parse-semver-1.1.1.tgz", + "integrity": "sha512-Eg1OuNntBMH0ojvEKSrvDSnwLmvVuUOSdylH/pSCPNMIspLlweJyIWXCE+k/5hm3cj/EBUYwmWkjhBALNP4LXQ==", + "dev": true, + "dependencies": { + "semver": "^5.1.0" + } + }, + "node_modules/parse-semver/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "dependencies": { + "entities": "^4.4.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", + "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", + "dependencies": { + "domhandler": "^5.0.2", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter/node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/parse5/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "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==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==" + }, + "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==", + "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==" + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true + }, + "node_modules/pg": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.8.0.tgz", + "integrity": "sha512-UXYN0ziKj+AeNNP7VDMwrehpACThH7LUl/p8TDFpEUuSejCUIwGSfxpHsPvtM6/WXFy6SU4E5RG4IJV/TZAGjw==", + "dependencies": { + "buffer-writer": "2.0.0", + "packet-reader": "1.0.0", + "pg-connection-string": "^2.5.0", + "pg-pool": "^3.5.2", + "pg-protocol": "^1.5.0", + "pg-types": "^2.1.0", + "pgpass": "1.x" + }, + "engines": { + "node": ">= 8.0.0" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-connection-string": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz", + "integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.5.2.tgz", + "integrity": "sha512-His3Fh17Z4eg7oANLob6ZvH8xIVen3phEZh2QuyrIl4dQSDVEabNducv6ysROKpDNPSD+12tONZVWfSgMvDD9w==", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.5.0.tgz", + "integrity": "sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ==" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "dependencies": { + "split2": "^4.1.0" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "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/pify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-5.0.0.tgz", + "integrity": "sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.1.0.tgz", + "integrity": "sha512-/RpmvKdxKf8uILTtoOhAgf30wYbP2Qw+L9p3Rvshx1JZVX+XQNZQFjlbmGHEGIm4CkVPlSn+NXmIM8+9oWQaSA==", + "dev": true, + "dependencies": { + "confbox": "^0.1.7", + "mlly": "^1.6.1", + "pathe": "^1.1.2" + } + }, + "node_modules/pkg-up": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", + "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-up/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==", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/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==", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/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==", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-up/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==", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/portfinder": { + "version": "1.0.32", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.32.tgz", + "integrity": "sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg==", + "dev": true, + "dependencies": { + "async": "^2.6.4", + "debug": "^3.2.7", + "mkdirp": "^0.5.6" + }, + "engines": { + "node": ">= 0.12.0" + } + }, + "node_modules/portfinder/node_modules/async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dev": true, + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/portfinder/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/portfinder/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/postcss": { + "version": "8.4.33", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz", + "integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==", + "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.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-calc": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz", + "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==", + "dependencies": { + "postcss-selector-parser": "^6.0.9", + "postcss-value-parser": "^4.2.0" + }, + "peerDependencies": { + "postcss": "^8.2.2" + } + }, + "node_modules/postcss-colormin": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.1.tgz", + "integrity": "sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ==", + "dependencies": { + "browserslist": "^4.21.4", + "caniuse-api": "^3.0.0", + "colord": "^2.9.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-convert-values": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.3.tgz", + "integrity": "sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==", + "dependencies": { + "browserslist": "^4.21.4", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-comments": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz", + "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-duplicates": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz", + "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-empty": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz", + "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-overridden": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz", + "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-unused": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-5.1.0.tgz", + "integrity": "sha512-KwLWymI9hbwXmJa0dkrzpRbSJEh0vVUd7r8t0yOGPcfKzyJJxFM8kLyC5Ev9avji6nY95pOp1W6HqIrfT+0VGw==", + "dependencies": { + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-import": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz", + "integrity": "sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-load-config": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", + "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", + "dev": true, + "dependencies": { + "lilconfig": "^2.0.5", + "yaml": "^1.10.2" + }, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-loader": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz", + "integrity": "sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==", + "dev": true, + "dependencies": { + "cosmiconfig": "^7.0.0", + "klona": "^2.0.5", + "semver": "^7.3.5" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "postcss": "^7.0.0 || ^8.0.1", + "webpack": "^5.0.0" + } + }, + "node_modules/postcss-loader/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/postcss-merge-idents": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-5.1.1.tgz", + "integrity": "sha512-pCijL1TREiCoog5nQp7wUe+TUonA2tC2sQ54UGeMmryK3UFGIYKqDyjnqd6RcuI4znFn9hWSLNN8xKE/vWcUQw==", + "dependencies": { + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-merge-longhand": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.7.tgz", + "integrity": "sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==", + "dependencies": { + "postcss-value-parser": "^4.2.0", + "stylehacks": "^5.1.1" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-merge-rules": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.4.tgz", + "integrity": "sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g==", + "dependencies": { + "browserslist": "^4.21.4", + "caniuse-api": "^3.0.0", + "cssnano-utils": "^3.1.0", + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-font-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz", + "integrity": "sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-gradients": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz", + "integrity": "sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==", + "dependencies": { + "colord": "^2.9.1", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-params": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.4.tgz", + "integrity": "sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==", + "dependencies": { + "browserslist": "^4.21.4", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-selectors": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz", + "integrity": "sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==", + "dependencies": { + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-modules": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/postcss-modules/-/postcss-modules-4.3.1.tgz", + "integrity": "sha512-ItUhSUxBBdNamkT3KzIZwYNNRFKmkJrofvC2nWab3CPKhYBQ1f27XXh1PAPE27Psx58jeelPsxWB/+og+KEH0Q==", + "dev": true, + "dependencies": { + "generic-names": "^4.0.0", + "icss-replace-symbols": "^1.1.0", + "lodash.camelcase": "^4.3.0", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "string-hash": "^1.1.1" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", + "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", + "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-normalize-charset": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz", + "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-display-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz", + "integrity": "sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-positions": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz", + "integrity": "sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-repeat-style": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz", + "integrity": "sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-string": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz", + "integrity": "sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-timing-functions": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz", + "integrity": "sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-unicode": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.1.tgz", + "integrity": "sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==", + "dependencies": { + "browserslist": "^4.21.4", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz", + "integrity": "sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==", + "dependencies": { + "normalize-url": "^6.0.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-url/node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/postcss-normalize-whitespace": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz", + "integrity": "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-ordered-values": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz", + "integrity": "sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==", + "dependencies": { + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-reduce-idents": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-5.2.0.tgz", + "integrity": "sha512-BTrLjICoSB6gxbc58D5mdBK8OhXRDqud/zodYfdSi52qvDHdMwk+9kB9xsM8yJThH/sZU5A6QVSmMmaN001gIg==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-reduce-initial": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.2.tgz", + "integrity": "sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg==", + "dependencies": { + "browserslist": "^4.21.4", + "caniuse-api": "^3.0.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-reduce-transforms": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz", + "integrity": "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.15", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz", + "integrity": "sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-sort-media-queries": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/postcss-sort-media-queries/-/postcss-sort-media-queries-4.4.1.tgz", + "integrity": "sha512-QDESFzDDGKgpiIh4GYXsSy6sek2yAwQx1JASl5AxBtU1Lq2JfKBljIPNdil989NcSKRQX1ToiaKphImtBuhXWw==", + "dependencies": { + "sort-css-media-queries": "2.1.0" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "postcss": "^8.4.16" + } + }, + "node_modules/postcss-svgo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.1.0.tgz", + "integrity": "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==", + "dependencies": { + "postcss-value-parser": "^4.2.0", + "svgo": "^2.7.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-unique-selectors": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz", + "integrity": "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==", + "dependencies": { + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/postcss-zindex": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-5.1.0.tgz", + "integrity": "sha512-fgFMf0OtVSBR1va1JNHYgMxYk73yhn/qb4uQDq1DLGYolz8gHCyr/sesEuGUaYs58E3ZJRcpoGuPVoB7Meiq9A==", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/prebuild-install": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", + "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==", + "dev": true, + "optional": true, + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.7.tgz", + "integrity": "sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/pretty-error": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", + "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", + "dependencies": { + "lodash": "^4.17.20", + "renderkid": "^3.0.0" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/pretty-time": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pretty-time/-/pretty-time-1.1.0.tgz", + "integrity": "sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/prism-react-renderer": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-1.3.5.tgz", + "integrity": "sha512-IJ+MSwBWKG+SM3b2SUfdrhC+gu01QkV2KmRQgREThBfSQRoufqRfxfHUxpG1WcaFjP+kojcFyO9Qqtpgt3qLCg==", + "peerDependencies": { + "react": ">=0.14.9" + } + }, + "node_modules/prismjs": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", + "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==", + "engines": { + "node": ">=6" + } + }, + "node_modules/proc-log": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-3.0.0.tgz", + "integrity": "sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "node_modules/promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "dependencies": { + "asap": "~2.0.3" + } + }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "optional": true + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "optional": true, + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/promise-retry/node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "optional": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/promise.series": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/promise.series/-/promise.series-0.2.0.tgz", + "integrity": "sha512-VWQJyU2bcDTgZw8kpfBpB/ejZASlCrzwz5f2hjb/zlujOEB4oeiAhHygAWq8ubsX2GVkD4kCU5V2dwOTaCY5EQ==", + "dev": true, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/propagate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", + "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/property-information": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz", + "integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==", + "dependencies": { + "xtend": "^4.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/protobufjs": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.2.tgz", + "integrity": "sha512-++PrQIjrom+bFDPpfmqXfAGSQs40116JRrqqyf53dymUMvvb5d/LMRyicRoF1AUKoXVS1/IgJXlEgcpr4gTF3Q==", + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/protobufjs-cli": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/protobufjs-cli/-/protobufjs-cli-1.1.1.tgz", + "integrity": "sha512-VPWMgIcRNyQwWUv8OLPyGQ/0lQY/QTQAVN5fh+XzfDwsVw1FZ2L3DM/bcBf8WPiRz2tNpaov9lPZfNcmNo6LXA==", + "dependencies": { + "chalk": "^4.0.0", + "escodegen": "^1.13.0", + "espree": "^9.0.0", + "estraverse": "^5.1.0", + "glob": "^8.0.0", + "jsdoc": "^4.0.0", + "minimist": "^1.2.0", + "semver": "^7.1.2", + "tmp": "^0.2.1", + "uglify-js": "^3.7.7" + }, + "bin": { + "pbjs": "bin/pbjs", + "pbts": "bin/pbts" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "protobufjs": "^7.0.0" + } + }, + "node_modules/protobufjs-cli/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/protobufjs-cli/node_modules/escodegen": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=4.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/protobufjs-cli/node_modules/escodegen/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/protobufjs-cli/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/protobufjs-cli/node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/protobufjs-cli/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/protobufjs-cli/node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/protobufjs-cli/node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/protobufjs-cli/node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-addr/node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true + }, + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", + "dev": true, + "optional": true + }, + "node_modules/psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", + "dev": true + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/pupa": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", + "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", + "dependencies": { + "escape-goat": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pure-color": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/pure-color/-/pure-color-1.3.0.tgz", + "integrity": "sha512-QFADYnsVoBMw1srW7OVKEYjG+MbIa49s54w1MA1EDY6r2r/sTcKKYqRX1f4GYvnXP7eN/Pe9HFcX+hwzmrXRHA==" + }, + "node_modules/pure-rand": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.4.tgz", + "integrity": "sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ] + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true + }, + "node_modules/queue": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", + "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", + "dependencies": { + "inherits": "~2.0.3" + } + }, + "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/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/railroad-diagrams": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz", + "integrity": "sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A==", + "dev": true + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-base16-styling": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/react-base16-styling/-/react-base16-styling-0.6.0.tgz", + "integrity": "sha512-yvh/7CArceR/jNATXOKDlvTnPKPmGZz7zsenQ3jUwLzHkNUR0CvY3yGYJbWJ/nnxsL8Sgmt5cO3/SILVuPO6TQ==", + "dependencies": { + "base16": "^1.0.0", + "lodash.curry": "^4.0.1", + "lodash.flow": "^3.3.0", + "pure-color": "^1.2.0" + } + }, + "node_modules/react-dev-utils": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", + "integrity": "sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==", + "dependencies": { + "@babel/code-frame": "^7.16.0", + "address": "^1.1.2", + "browserslist": "^4.18.1", + "chalk": "^4.1.2", + "cross-spawn": "^7.0.3", + "detect-port-alt": "^1.1.6", + "escape-string-regexp": "^4.0.0", + "filesize": "^8.0.6", + "find-up": "^5.0.0", + "fork-ts-checker-webpack-plugin": "^6.5.0", + "global-modules": "^2.0.0", + "globby": "^11.0.4", + "gzip-size": "^6.0.0", + "immer": "^9.0.7", + "is-root": "^2.1.0", + "loader-utils": "^3.2.0", + "open": "^8.4.0", + "pkg-up": "^3.1.0", + "prompts": "^2.4.2", + "react-error-overlay": "^6.0.11", + "recursive-readdir": "^2.2.2", + "shell-quote": "^1.7.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/react-dev-utils/node_modules/cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/react-dev-utils/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==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-dev-utils/node_modules/fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/react-dev-utils/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==", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-dev-utils/node_modules/fork-ts-checker-webpack-plugin": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.2.tgz", + "integrity": "sha512-m5cUmF30xkZ7h4tWUgTAcEaKmUW7tfyUyTqNNOz7OxWJ0v1VWKTcOvH8FWHUwSjlW/356Ijc9vi3XfcPstpQKA==", + "dependencies": { + "@babel/code-frame": "^7.8.3", + "@types/json-schema": "^7.0.5", + "chalk": "^4.1.0", + "chokidar": "^3.4.2", + "cosmiconfig": "^6.0.0", + "deepmerge": "^4.2.2", + "fs-extra": "^9.0.0", + "glob": "^7.1.6", + "memfs": "^3.1.2", + "minimatch": "^3.0.4", + "schema-utils": "2.7.0", + "semver": "^7.3.2", + "tapable": "^1.0.0" + }, + "engines": { + "node": ">=10", + "yarn": ">=1.0.0" + }, + "peerDependencies": { + "eslint": ">= 6", + "typescript": ">= 2.7", + "vue-template-compiler": "*", + "webpack": ">= 4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + }, + "vue-template-compiler": { + "optional": true + } + } + }, + "node_modules/react-dev-utils/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/react-dev-utils/node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-dev-utils/node_modules/loader-utils": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz", + "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==", + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/react-dev-utils/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==", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-dev-utils/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==", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-dev-utils/node_modules/schema-utils": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", + "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", + "dependencies": { + "@types/json-schema": "^7.0.4", + "ajv": "^6.12.2", + "ajv-keywords": "^3.4.1" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/react-dev-utils/node_modules/tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + }, + "peerDependencies": { + "react": "^18.2.0" + } + }, + "node_modules/react-error-overlay": { + "version": "6.0.11", + "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", + "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" + }, + "node_modules/react-fast-compare": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz", + "integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==" + }, + "node_modules/react-helmet-async": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-1.3.0.tgz", + "integrity": "sha512-9jZ57/dAn9t3q6hneQS0wukqC2ENOBgMNVEhb/ZG9ZSxUetzVIw4iAmEU38IaVg3QGYauQPhSeUTuIUtFglWpg==", + "dependencies": { + "@babel/runtime": "^7.12.5", + "invariant": "^2.2.4", + "prop-types": "^15.7.2", + "react-fast-compare": "^3.2.0", + "shallowequal": "^1.1.0" + }, + "peerDependencies": { + "react": "^16.6.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.6.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/react-json-view": { + "version": "1.21.3", + "resolved": "https://registry.npmjs.org/react-json-view/-/react-json-view-1.21.3.tgz", + "integrity": "sha512-13p8IREj9/x/Ye4WI/JpjhoIwuzEgUAtgJZNBJckfzJt1qyh24BdTm6UQNGnyTq9dapQdrqvquZTo3dz1X6Cjw==", + "dependencies": { + "flux": "^4.0.1", + "react-base16-styling": "^0.6.0", + "react-lifecycles-compat": "^3.0.4", + "react-textarea-autosize": "^8.3.2" + }, + "peerDependencies": { + "react": "^17.0.0 || ^16.3.0 || ^15.5.4", + "react-dom": "^17.0.0 || ^16.3.0 || ^15.5.4" + } + }, + "node_modules/react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, + "node_modules/react-loadable": { + "name": "@docusaurus/react-loadable", + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz", + "integrity": "sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ==", + "dependencies": { + "@types/react": "*", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": "*" + } + }, + "node_modules/react-loadable-ssr-addon-v5-slorber": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/react-loadable-ssr-addon-v5-slorber/-/react-loadable-ssr-addon-v5-slorber-1.0.1.tgz", + "integrity": "sha512-lq3Lyw1lGku8zUEJPDxsNm1AfYHBrO9Y1+olAYwpUJ2IGFBskM0DMKok97A6LWUpHm+o7IvQBOWu9MLenp9Z+A==", + "dependencies": { + "@babel/runtime": "^7.10.3" + }, + "engines": { + "node": ">=10.13.0" + }, + "peerDependencies": { + "react-loadable": "*", + "webpack": ">=4.41.1 || 5.x" + } + }, + "node_modules/react-router": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.3.4.tgz", + "integrity": "sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA==", + "dependencies": { + "@babel/runtime": "^7.12.13", + "history": "^4.9.0", + "hoist-non-react-statics": "^3.1.0", + "loose-envify": "^1.3.1", + "path-to-regexp": "^1.7.0", + "prop-types": "^15.6.2", + "react-is": "^16.6.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0" + }, + "peerDependencies": { + "react": ">=15" + } + }, + "node_modules/react-router-config": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/react-router-config/-/react-router-config-5.1.1.tgz", + "integrity": "sha512-DuanZjaD8mQp1ppHjgnnUnyOlqYXZVjnov/JzFhjLEwd3Z4dYjMSnqrEzzGThH47vpCOqPPwJM2FtthLeJ8Pbg==", + "dependencies": { + "@babel/runtime": "^7.1.2" + }, + "peerDependencies": { + "react": ">=15", + "react-router": ">=5" + } + }, + "node_modules/react-router-dom": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.3.4.tgz", + "integrity": "sha512-m4EqFMHv/Ih4kpcBCONHbkT68KoAeHN4p3lAGoNryfHi0dMy0kCzEZakiKRsvg5wHZ/JLrLW8o8KomWiz/qbYQ==", + "dependencies": { + "@babel/runtime": "^7.12.13", + "history": "^4.9.0", + "loose-envify": "^1.3.1", + "prop-types": "^15.6.2", + "react-router": "5.3.4", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0" + }, + "peerDependencies": { + "react": ">=15" + } + }, + "node_modules/react-router/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" + }, + "node_modules/react-router/node_modules/path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "dependencies": { + "isarray": "0.0.1" + } + }, + "node_modules/react-router/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/react-textarea-autosize": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.4.1.tgz", + "integrity": "sha512-aD2C+qK6QypknC+lCMzteOdIjoMbNlgSFmJjCV+DrfTPwp59i/it9mMNf2HDzvRjQgKAyBDPyLJhcrzElf2U4Q==", + "dependencies": { + "@babel/runtime": "^7.20.13", + "use-composed-ref": "^1.3.0", + "use-latest": "^1.2.1" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/read": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", + "integrity": "sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ==", + "dev": true, + "dependencies": { + "mute-stream": "~0.0.4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/read-cache/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdir-glob": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "dependencies": { + "minimatch": "^5.1.0" + } + }, + "node_modules/readdir-glob/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/readdir-glob/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/reading-time": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/reading-time/-/reading-time-1.5.0.tgz", + "integrity": "sha512-onYyVhBNr4CmAxFsKS7bz+uTLRakypIe4R+5A824vBSkQy/hB3fZepoVEf8OVAxzLvK+H/jm9TzpI3ETSm64Kg==" + }, + "node_modules/rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", + "dependencies": { + "resolve": "^1.1.6" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/recursive-readdir": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", + "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", + "dependencies": { + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz", + "integrity": "sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==", + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, + "node_modules/regenerator-transform": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", + "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", + "dependencies": { + "@babel/runtime": "^7.8.4" + } + }, + "node_modules/regexp-tree": { + "version": "0.1.27", + "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", + "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==", + "dev": true, + "bin": { + "regexp-tree": "bin/regexp-tree" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpu-core": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", + "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", + "dependencies": { + "@babel/regjsgen": "^0.8.0", + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.1.0", + "regjsparser": "^0.9.1", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/registry-auth-token": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.2.tgz", + "integrity": "sha512-PC5ZysNb42zpFME6D/XlIgtNGdTl8bBOCw90xQLVMpzuuubJKYDWFAEuUNc+Cn8Z8724tg2SDhDRrkVEsqfDMg==", + "dependencies": { + "rc": "1.2.8" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/registry-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", + "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", + "dependencies": { + "rc": "^1.2.8" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/regjsparser": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "bin": { + "jsesc": "bin/jsesc" + } + }, + "node_modules/relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/remark-emoji": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/remark-emoji/-/remark-emoji-2.2.0.tgz", + "integrity": "sha512-P3cj9s5ggsUvWw5fS2uzCHJMGuXYRb0NnZqYlNecewXt8QBU9n5vW3DUUKOhepS8F9CwdMx9B8a3i7pqFWAI5w==", + "dependencies": { + "emoticon": "^3.2.0", + "node-emoji": "^1.10.0", + "unist-util-visit": "^2.0.3" + } + }, + "node_modules/remark-footnotes": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/remark-footnotes/-/remark-footnotes-2.0.0.tgz", + "integrity": "sha512-3Clt8ZMH75Ayjp9q4CorNeyjwIxHFcTkaektplKGl2A1jNGEUey8cKL0ZC5vJwfcD5GFGsNLImLG/NGzWIzoMQ==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-mdx": { + "version": "1.6.22", + "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-1.6.22.tgz", + "integrity": "sha512-phMHBJgeV76uyFkH4rvzCftLfKCr2RZuF+/gmVcaKrpsihyzmhXjA0BEMDaPTXG5y8qZOKPVo83NAOX01LPnOQ==", + "dependencies": { + "@babel/core": "7.12.9", + "@babel/helper-plugin-utils": "7.10.4", + "@babel/plugin-proposal-object-rest-spread": "7.12.1", + "@babel/plugin-syntax-jsx": "7.12.1", + "@mdx-js/util": "1.6.22", + "is-alphabetical": "1.0.4", + "remark-parse": "8.0.3", + "unified": "9.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-mdx/node_modules/@babel/core": { + "version": "7.12.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.9.tgz", + "integrity": "sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ==", + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.12.5", + "@babel/helper-module-transforms": "^7.12.1", + "@babel/helpers": "^7.12.5", + "@babel/parser": "^7.12.7", + "@babel/template": "^7.12.7", + "@babel/traverse": "^7.12.9", + "@babel/types": "^7.12.7", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.2", + "lodash": "^4.17.19", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/remark-mdx/node_modules/@babel/helper-plugin-utils": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", + "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==" + }, + "node_modules/remark-mdx/node_modules/@babel/plugin-proposal-object-rest-spread": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.12.1.tgz", + "integrity": "sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.0", + "@babel/plugin-transform-parameters": "^7.12.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/remark-mdx/node_modules/@babel/plugin-syntax-jsx": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.1.tgz", + "integrity": "sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/remark-mdx/node_modules/is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "engines": { + "node": ">=4" + } + }, + "node_modules/remark-mdx/node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/remark-mdx/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/remark-mdx/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/remark-mdx/node_modules/unified": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.0.tgz", + "integrity": "sha512-vx2Z0vY+a3YoTj8+pttM3tiJHCwY5UFbYdiWrwBEbHmK8pvsPj2rtAX2BFfgXen8T39CJWblWRDT4L5WGXtDdg==", + "dependencies": { + "bail": "^1.0.0", + "extend": "^3.0.0", + "is-buffer": "^2.0.0", + "is-plain-obj": "^2.0.0", + "trough": "^1.0.0", + "vfile": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-parse": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-8.0.3.tgz", + "integrity": "sha512-E1K9+QLGgggHxCQtLt++uXltxEprmWzNfg+MxpfHsZlrddKzZ/hZyWHDbK3/Ap8HJQqYJRXP+jHczdL6q6i85Q==", + "dependencies": { + "ccount": "^1.0.0", + "collapse-white-space": "^1.0.2", + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-whitespace-character": "^1.0.0", + "is-word-character": "^1.0.0", + "markdown-escapes": "^1.0.0", + "parse-entities": "^2.0.0", + "repeat-string": "^1.5.4", + "state-toggle": "^1.0.0", + "trim": "0.0.1", + "trim-trailing-lines": "^1.0.0", + "unherit": "^1.0.4", + "unist-util-remove-position": "^2.0.0", + "vfile-location": "^3.0.0", + "xtend": "^4.0.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-squeeze-paragraphs": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/remark-squeeze-paragraphs/-/remark-squeeze-paragraphs-4.0.0.tgz", + "integrity": "sha512-8qRqmL9F4nuLPIgl92XUuxI3pFxize+F1H0e/W3llTk0UsjJaj01+RrirkMw7P21RKe4X6goQhYRSvNWX+70Rw==", + "dependencies": { + "mdast-squeeze-paragraphs": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/renderkid": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", + "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", + "dependencies": { + "css-select": "^4.1.3", + "dom-converter": "^0.2.0", + "htmlparser2": "^6.1.0", + "lodash": "^4.17.21", + "strip-ansi": "^6.0.1" + } + }, + "node_modules/renderkid/node_modules/htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "engines": { + "node": ">=0.10" + } + }, + "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-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-like": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz", + "integrity": "sha512-oyrU88skkMtDdauHDuKVrgR+zuItqr6/c//FXzvmxRGMexSDc6hNvJInGW3LL46n+8b50RykrvwSUIIQH2LQ5A==", + "engines": { + "node": "*" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" + }, + "node_modules/requizzle": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz", + "integrity": "sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==", + "dependencies": { + "lodash": "^4.17.21" + } + }, + "node_modules/resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dependencies": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==" + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-pathname": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz", + "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==" + }, + "node_modules/resolve.exports": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", + "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/robust-predicates": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", + "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==" + }, + "node_modules/rollup": { + "version": "2.79.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", + "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", + "dev": true, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=10.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/rollup-plugin-copy": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-copy/-/rollup-plugin-copy-3.5.0.tgz", + "integrity": "sha512-wI8D5dvYovRMx/YYKtUNt3Yxaw4ORC9xo6Gt9t22kveWz1enG9QrhVlagzwrxSC455xD1dHMKhIJkbsQ7d48BA==", + "dev": true, + "dependencies": { + "@types/fs-extra": "^8.0.1", + "colorette": "^1.1.0", + "fs-extra": "^8.1.0", + "globby": "10.0.1", + "is-plain-object": "^3.0.0" + }, + "engines": { + "node": ">=8.3" + } + }, + "node_modules/rollup-plugin-copy/node_modules/colorette": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", + "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", + "dev": true + }, + "node_modules/rollup-plugin-copy/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/rollup-plugin-copy/node_modules/is-plain-object": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.1.tgz", + "integrity": "sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rollup-plugin-copy/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/rollup-plugin-copy/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/rollup-plugin-peer-deps-external": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/rollup-plugin-peer-deps-external/-/rollup-plugin-peer-deps-external-2.2.4.tgz", + "integrity": "sha512-AWdukIM1+k5JDdAqV/Cxd+nejvno2FVLVeZ74NKggm3Q5s9cbbcOgUPGdbxPi4BXu7xGaZ8HG12F+thImYu/0g==", + "dev": true, + "peerDependencies": { + "rollup": "*" + } + }, + "node_modules/rollup-plugin-postcss": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/rollup-plugin-postcss/-/rollup-plugin-postcss-4.0.2.tgz", + "integrity": "sha512-05EaY6zvZdmvPUDi3uCcAQoESDcYnv8ogJJQRp6V5kZ6J6P7uAVJlrTZcaaA20wTH527YTnKfkAoPxWI/jPp4w==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "concat-with-sourcemaps": "^1.1.0", + "cssnano": "^5.0.1", + "import-cwd": "^3.0.0", + "p-queue": "^6.6.2", + "pify": "^5.0.0", + "postcss-load-config": "^3.0.0", + "postcss-modules": "^4.0.0", + "promise.series": "^0.2.0", + "resolve": "^1.19.0", + "rollup-pluginutils": "^2.8.2", + "safe-identifier": "^0.4.2", + "style-inject": "^0.3.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "postcss": "8.x" + } + }, + "node_modules/rollup-plugin-typescript2": { + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/rollup-plugin-typescript2/-/rollup-plugin-typescript2-0.34.1.tgz", + "integrity": "sha512-P4cHLtGikESmqi1CA+tdMDUv8WbQV48mzPYt77TSTOPJpERyZ9TXdDgjSDix8Fkqce6soYz3+fa4lrC93IEkcw==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^4.1.2", + "find-cache-dir": "^3.3.2", + "fs-extra": "^10.0.0", + "semver": "^7.3.7", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "rollup": ">=1.26.3", + "typescript": ">=2.4.0" + } + }, + "node_modules/rollup-plugin-typescript2/node_modules/@rollup/pluginutils": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz", + "integrity": "sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==", + "dev": true, + "dependencies": { + "estree-walker": "^2.0.1", + "picomatch": "^2.2.2" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/rollup-plugin-typescript2/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/rollup-pluginutils": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz", + "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==", + "dev": true, + "dependencies": { + "estree-walker": "^0.6.1" + } + }, + "node_modules/rollup-pluginutils/node_modules/estree-walker": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", + "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==", + "dev": true + }, + "node_modules/rtl-detect": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/rtl-detect/-/rtl-detect-1.0.4.tgz", + "integrity": "sha512-EBR4I2VDSSYr7PkBmFy04uhycIpDKp+21p/jARYXlCSjQksTBQcJ0HFUPOO79EPPH5JS6VAhiIQbycf0O3JAxQ==" + }, + "node_modules/rtlcss": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-3.5.0.tgz", + "integrity": "sha512-wzgMaMFHQTnyi9YOwsx9LjOxYXJPzS8sYnFaKm6R5ysvTkwzHiB0vxnbHwchHQT65PTdBjDG21/kQBWI7q9O7A==", + "dependencies": { + "find-up": "^5.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.3.11", + "strip-json-comments": "^3.1.1" + }, + "bin": { + "rtlcss": "bin/rtlcss.js" + } + }, + "node_modules/rtlcss/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==", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/rtlcss/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==", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/rtlcss/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==", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "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/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" + }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-identifier": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/safe-identifier/-/safe-identifier-0.4.2.tgz", + "integrity": "sha512-6pNbSMW6OhAi9j+N8V+U715yBQsaWJ7eyEUaOrawX+isg5ZxhUlV1NipNtgaKHmFGiABwt+ZF04Ii+3Xjkg+8w==", + "dev": true + }, + "node_modules/safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/sass": { + "version": "1.70.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.70.0.tgz", + "integrity": "sha512-uUxNQ3zAHeAx5nRFskBnrWzDUJrrvpCPD5FNAoRvTi0WwremlheES3tg+56PaVtCs5QDRX5CBLxxKMDJMEa1WQ==", + "dev": true, + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-loader": { + "version": "12.6.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz", + "integrity": "sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA==", + "dev": true, + "dependencies": { + "klona": "^2.0.4", + "neo-async": "^2.6.2" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "fibers": ">= 3.1.0", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", + "sass": "^1.3.0", + "sass-embedded": "*", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "fibers": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + } + } + }, + "node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "node_modules/saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/scheduler": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dependencies": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/section-matter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", + "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==", + "dependencies": { + "extend-shallow": "^2.0.1", + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/secure-compare": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz", + "integrity": "sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==", + "dev": true + }, + "node_modules/select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==" + }, + "node_modules/selfsigned": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.1.1.tgz", + "integrity": "sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ==", + "dependencies": { + "node-forge": "^1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver-diff": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", + "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", + "dependencies": { + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/semver-diff/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/send/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==" + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serve-handler": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.5.tgz", + "integrity": "sha512-ijPFle6Hwe8zfmBxJdE+5fta53fdIY0lHISJvuikXB3VYFafRjMRpOffSPvCYsbKyBA7pvy9oYr/BT1O3EArlg==", + "dependencies": { + "bytes": "3.0.0", + "content-disposition": "0.5.2", + "fast-url-parser": "1.1.3", + "mime-types": "2.1.18", + "minimatch": "3.1.2", + "path-is-inside": "1.0.2", + "path-to-regexp": "2.2.1", + "range-parser": "1.2.0" + } + }, + "node_modules/serve-handler/node_modules/content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-handler/node_modules/mime-db": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", + "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-handler/node_modules/mime-types": { + "version": "2.1.18", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", + "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", + "dependencies": { + "mime-db": "~1.33.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-handler/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/serve-handler/node_modules/path-to-regexp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.2.1.tgz", + "integrity": "sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ==" + }, + "node_modules/serve-handler/node_modules/range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", + "dependencies": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-index/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/serve-index/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" + }, + "node_modules/serve-index/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/serve-index/node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "node_modules/serve-index/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "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==" + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + }, + "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==", + "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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.0.tgz", + "integrity": "sha512-QHsz8GgQIGKlRi24yFc6a6lN69Idnx634w49ay6+jA5yFh7a1UY+4Rp6HPx/L/1zcEDPEij8cIsiqR6bQsE5VQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/shelljs": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", + "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", + "dependencies": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + }, + "bin": { + "shjs": "bin/shjs" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true + }, + "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==" + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "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" + } + ], + "optional": true + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "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" + } + ], + "optional": true, + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/sirv": { + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-1.0.19.tgz", + "integrity": "sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ==", + "dependencies": { + "@polka/url": "^1.0.0-next.20", + "mrmime": "^1.0.0", + "totalist": "^1.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" + }, + "node_modules/sitemap": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-7.1.1.tgz", + "integrity": "sha512-mK3aFtjz4VdJN0igpIJrinf3EO8U8mxOPsTBzSsy06UtjZQJ3YY3o3Xa7zSc5nMqcMrRwlChHZ18Kxg0caiPBg==", + "dependencies": { + "@types/node": "^17.0.5", + "@types/sax": "^1.2.1", + "arg": "^5.0.0", + "sax": "^1.2.4" + }, + "bin": { + "sitemap": "dist/cli.js" + }, + "engines": { + "node": ">=12.0.0", + "npm": ">=5.6.0" + } + }, + "node_modules/sitemap/node_modules/@types/node": { + "version": "17.0.45", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz", + "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==" + }, + "node_modules/sitemap/node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "optional": true, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/snake-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", + "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "dev": true, + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "dependencies": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "node_modules/socks": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", + "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", + "optional": true, + "dependencies": { + "ip": "^2.0.0", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.13.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", + "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", + "optional": true, + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/sort-css-media-queries": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/sort-css-media-queries/-/sort-css-media-queries-2.1.0.tgz", + "integrity": "sha512-IeWvo8NkNiY2vVYdPa27MCQiR0MN0M80johAYFVxWWXQ44KU84WNxjslwBHmc/7ZL2ccwkM7/e6S5aiKZXm7jA==", + "engines": { + "node": ">= 6.3.0" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-loader": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-3.0.2.tgz", + "integrity": "sha512-BokxPoLjyl3iOrgkWaakaxqnelAJSS+0V+De0kKIq6lyWrXuiPgYTGp6z3iHmqljKAaLXwZa+ctD8GccRJeVvg==", + "dev": true, + "dependencies": { + "abab": "^2.0.5", + "iconv-lite": "^0.6.3", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "deprecated": "Please use @jridgewell/sourcemap-codec instead", + "dev": true + }, + "node_modules/space-separated-tokens": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz", + "integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.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": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz", + "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==", + "dev": true + }, + "node_modules/spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dependencies": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dependencies": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "node_modules/split2": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz", + "integrity": "sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ==", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + }, + "node_modules/sqlite3": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.1.5.tgz", + "integrity": "sha512-7sP16i4wI+yKnGOO2q2ijze7EjQ9US+Vw7DYYwxfFtqNZDGgBcEw0oeDaDvUTq66uJOzVd/z6MkIg+c9erSJKg==", + "hasInstallScript": true, + "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.0", + "node-addon-api": "^4.2.0", + "tar": "^6.1.11" + }, + "optionalDependencies": { + "node-gyp": "8.x" + }, + "peerDependencies": { + "node-gyp": "8.x" + }, + "peerDependenciesMeta": { + "node-gyp": { + "optional": true + } + } + }, + "node_modules/sqlite3/node_modules/node-addon-api": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", + "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==" + }, + "node_modules/ssri": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "optional": true, + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility" + }, + "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/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true + }, + "node_modules/state-toggle": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/state-toggle/-/state-toggle-1.0.3.tgz", + "integrity": "sha512-d/5Z4/2iiCnHw6Xzghyhb+GcmF89bxwgXG60wjIiZaxnymbyOmI8Hk4VqHXiVVp6u2ysaskFfXg3ekCj4WNftQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/std-env": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz", + "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==" + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-hash": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/string-hash/-/string-hash-1.1.3.tgz", + "integrity": "sha512-kJUvRUFK49aub+a7T1nNE66EJbZBMnBgoC1UbCZ5n6bsZKBRga4KgBRTMn/pFkeCZSYtNeSyMxPDM0AXWELk2A==", + "dev": true + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-natural-compare": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/string-natural-compare/-/string-natural-compare-3.0.1.tgz", + "integrity": "sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==", + "dev": true + }, + "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.prototype.matchall": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz", + "integrity": "sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "regexp.prototype.flags": "^1.4.3", + "side-channel": "^1.0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", + "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", + "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "dependencies": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "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-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-bom-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", + "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.1.0.tgz", + "integrity": "sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw==", + "dev": true, + "dependencies": { + "js-tokens": "^9.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/strip-literal/node_modules/js-tokens": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.0.tgz", + "integrity": "sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==", + "dev": true + }, + "node_modules/strong-log-transformer": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz", + "integrity": "sha512-B3Hgul+z0L9a236FAUC9iZsL+nVHgoCJnqCbN588DjYxvGXaXaaFbfmQ/JhvKjZwsOukuR72XbHv71Qkug0HxA==", + "dev": true, + "dependencies": { + "duplexer": "^0.1.1", + "minimist": "^1.2.0", + "through": "^2.3.4" + }, + "bin": { + "sl-log-transformer": "bin/sl-log-transformer.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/style-inject": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/style-inject/-/style-inject-0.3.0.tgz", + "integrity": "sha512-IezA2qp+vcdlhJaVm5SOdPPTUu0FCEqfNSli2vRuSIBbu5Nq5UvygTk/VzeCqfLz2Atj3dVII5QBKGZRZ0edzw==", + "dev": true + }, + "node_modules/style-loader": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.4.tgz", + "integrity": "sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w==", + "dev": true, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/style-to-object": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.3.0.tgz", + "integrity": "sha512-CzFnRRXhzWIdItT3OmF8SQfWyahHhjq3HwcMNCNLn+N7klOOqPjMeG/4JSu77D7ypZdGvSzvkrbyeTMizz2VrA==", + "dependencies": { + "inline-style-parser": "0.1.1" + } + }, + "node_modules/stylehacks": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", + "integrity": "sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==", + "dependencies": { + "browserslist": "^4.21.4", + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" + }, + "node_modules/stylus": { + "version": "0.59.0", + "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.59.0.tgz", + "integrity": "sha512-lQ9w/XIOH5ZHVNuNbWW8D822r+/wBSO/d6XvtyHLF7LW4KaCIDeVbvn5DF8fGCJAUCwVhVi/h6J0NUcnylUEjg==", + "dev": true, + "dependencies": { + "@adobe/css-tools": "^4.0.1", + "debug": "^4.3.2", + "glob": "^7.1.6", + "sax": "~1.2.4", + "source-map": "^0.7.3" + }, + "bin": { + "stylus": "bin/stylus" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://opencollective.com/stylus" + } + }, + "node_modules/stylus-loader": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-7.1.3.tgz", + "integrity": "sha512-TY0SKwiY7D2kMd3UxaWKSf3xHF0FFN/FAfsSqfrhxRT/koXTwffq2cgEWDkLQz7VojMu7qEEHt5TlMjkPx9UDw==", + "dev": true, + "dependencies": { + "fast-glob": "^3.2.12", + "normalize-path": "^3.0.0" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "stylus": ">=0.52.4", + "webpack": "^5.0.0" + } + }, + "node_modules/stylus-loader/node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/stylus/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "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==", + "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==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svg-parser": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", + "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==" + }, + "node_modules/svgo": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", + "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "dependencies": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^4.1.3", + "css-tree": "^1.1.3", + "csso": "^4.2.0", + "picocolors": "^1.0.0", + "stable": "^0.1.8" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/svgo/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/tar": { + "version": "6.1.12", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.12.tgz", + "integrity": "sha512-jU4TdemS31uABHd+Lt5WEYJuzn+TJTCBLljvIAHZOz6M9Os5pJ4dD+vRFLxPa/n3T0iEFzpi+0x1UfuDZYbRMw==", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "dev": true, + "optional": true, + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-fs/node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true, + "optional": true + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/terser": { + "version": "5.27.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.27.0.tgz", + "integrity": "sha512-bi1HRwVRskAjheeYl291n3JC4GgO/Ty4z1nVs5AAsmonJulGxpSektecnNedrwK9C7vpvVtcX3cw00VSLt7U2A==", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.10", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", + "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.20", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.1", + "terser": "^5.26.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin/node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/terser-webpack-plugin/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "node_modules/terser/node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, + "node_modules/thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" + }, + "node_modules/tiny-invariant": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", + "integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==" + }, + "node_modules/tiny-warning": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" + }, + "node_modules/tinybench": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.8.0.tgz", + "integrity": "sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw==", + "dev": true + }, + "node_modules/tinypool": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.4.tgz", + "integrity": "sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.1.tgz", + "integrity": "sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dependencies": { + "rimraf": "^3.0.0" + }, + "engines": { + "node": ">=8.17.0" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "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/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/totalist": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-1.1.0.tgz", + "integrity": "sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==", + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "dev": true, + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie/node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "dev": true, + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/traverse": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", + "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==", + "engines": { + "node": "*" + } + }, + "node_modules/trim": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.3.tgz", + "integrity": "sha512-h82ywcYhHK7veeelXrCScdH7HkWfbIT1D/CgYO+nmDarz3SGNssVBMws6jU16Ga60AJCRAvPV6w6RLuNerQqjg==", + "deprecated": "Use String.prototype.trim() instead" + }, + "node_modules/trim-trailing-lines": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-1.1.4.tgz", + "integrity": "sha512-rjUWSqnfTNrjbB9NQWfPMH/xRK1deHeGsHoVfpxJ++XeYXE0d6B1En37AHfw3jtfTU7dzMzZL2jjpe8Qb5gLIQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz", + "integrity": "sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/ts-api-utils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "dev": true, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/ts-dedent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz", + "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==", + "engines": { + "node": ">=6.10" + } + }, + "node_modules/ts-loader": { + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.1.tgz", + "integrity": "sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "enhanced-resolve": "^5.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4", + "source-map": "^0.7.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "typescript": "*", + "webpack": "^5.0.0" + } + }, + "node_modules/ts-loader/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/tsconfig-paths": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", + "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", + "dev": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths-webpack-plugin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.0.0.tgz", + "integrity": "sha512-fw/7265mIWukrSHd0i+wSwx64kYUSAKPfxRDksjKIYTxSAp9W9/xcZVBF4Kl0eqQd5eBpAQ/oQrc5RyM/0c1GQ==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "enhanced-resolve": "^5.7.0", + "tsconfig-paths": "^4.0.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/tsconfig-paths-webpack-plugin/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/tsconfig-paths-webpack-plugin/node_modules/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "dependencies": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/tsconfig-paths/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "dev": true, + "engines": { + "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + } + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dev": true, + "optional": true, + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "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.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typed-assert": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/typed-assert/-/typed-assert-1.0.9.tgz", + "integrity": "sha512-KNNZtayBCtmnNmbo5mG47p1XsCyrx6iVqomjcZnec/1Y5GGARaxPs6r49RnSPeUP3YjNYiU9sQHAtY4BBvnZwg==", + "dev": true + }, + "node_modules/typed-rest-client": { + "version": "1.8.9", + "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.8.9.tgz", + "integrity": "sha512-uSmjE38B80wjL85UFX3sTYEUlvZ1JgCRhsWj/fJ4rZ0FqDUFoIuodtiVeE+cUqiVTOKPdKrp/sdftD15MDek6g==", + "dev": true, + "dependencies": { + "qs": "^6.9.1", + "tunnel": "0.0.6", + "underscore": "^1.12.1" + } + }, + "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==", + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/ua-parser-js": { + "version": "1.0.35", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.35.tgz", + "integrity": "sha512-fKnGuqmTBnIE+/KXSzCn4db8RTigUzw1AN0DmdU6hJovUTbYJKyqj+8Mt1c4VfRDnOVJnENmfYkIPZ946UrSAA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + } + ], + "engines": { + "node": "*" + } + }, + "node_modules/uc.micro": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==" + }, + "node_modules/ufo": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.3.tgz", + "integrity": "sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==", + "dev": true + }, + "node_modules/uglify-js": { + "version": "3.17.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", + "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/underscore": { + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", + "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==" + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, + "node_modules/unherit": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/unherit/-/unherit-1.1.3.tgz", + "integrity": "sha512-Ft16BJcnapDKp0+J/rqFC3Rrk6Y/Ng4nzsC028k2jdDII/rdZ7Wd3pPT/6+vIIxRagwRc9K0IUX0Ra4fKvw+WQ==", + "dependencies": { + "inherits": "^2.0.0", + "xtend": "^4.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", + "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "engines": { + "node": ">=4" + } + }, + "node_modules/unified": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.2.tgz", + "integrity": "sha512-Sg7j110mtefBD+qunSLO1lqOEKdrwBFBrR6Qd8f4uwkhWNlbkaqwHse6e7QvD3AP/MNoJdEDLaf8OxYyoWgorQ==", + "dependencies": { + "bail": "^1.0.0", + "extend": "^3.0.0", + "is-buffer": "^2.0.0", + "is-plain-obj": "^2.0.0", + "trough": "^1.0.0", + "vfile": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unified/node_modules/is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "engines": { + "node": ">=4" + } + }, + "node_modules/unified/node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/union": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz", + "integrity": "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==", + "dev": true, + "dependencies": { + "qs": "^6.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "optional": true, + "dependencies": { + "unique-slug": "^2.0.0" + } + }, + "node_modules/unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "optional": true, + "dependencies": { + "imurmurhash": "^0.1.4" + } + }, + "node_modules/unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dependencies": { + "crypto-random-string": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/unist-builder": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-builder/-/unist-builder-2.0.3.tgz", + "integrity": "sha512-f98yt5pnlMWlzP539tPc4grGMsFaQQlP/vM396b00jngsiINumNmsY8rkXjfoi1c6QaM8nQ3vaGDuoKWbe/1Uw==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-generated": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-1.1.6.tgz", + "integrity": "sha512-cln2Mm1/CZzN5ttGK7vkoGw+RZ8VcUH6BtGbq98DDtRGquAAOXig1mrBQYelOwMXYS8rK+vZDyyojSjp7JX+Lg==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.1.0.tgz", + "integrity": "sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-3.1.0.tgz", + "integrity": "sha512-w+PkwCbYSFw8vpgWD0v7zRCl1FpY3fjDSQ3/N/wNd9Ffa4gPi8+4keqt99N3XW6F99t/mUzp2xAhNmfKWp95QA==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-remove": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unist-util-remove/-/unist-util-remove-2.1.0.tgz", + "integrity": "sha512-J8NYPyBm4baYLdCbjmf1bhPu45Cr1MWTm77qd9istEkzWpnN6O9tMsEbB2JhNnBCqGENRqEWomQ+He6au0B27Q==", + "dependencies": { + "unist-util-is": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-remove-position": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-2.0.1.tgz", + "integrity": "sha512-fDZsLYIe2uT+oGFnuZmy73K6ZxOPG/Qcm+w7jbEjaFcJgbQ6cqjs/eSPzXhsmGpAsWPkqZM9pYjww5QTn3LHMA==", + "dependencies": { + "unist-util-visit": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz", + "integrity": "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==", + "dependencies": { + "@types/unist": "^2.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.3.tgz", + "integrity": "sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^4.0.0", + "unist-util-visit-parents": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz", + "integrity": "sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/unzipper": { + "version": "0.10.14", + "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.14.tgz", + "integrity": "sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g==", + "dependencies": { + "big-integer": "^1.6.17", + "binary": "~0.3.0", + "bluebird": "~3.4.1", + "buffer-indexof-polyfill": "~1.0.0", + "duplexer2": "~0.1.4", + "fstream": "^1.0.12", + "graceful-fs": "^4.2.2", + "listenercount": "~1.0.1", + "readable-stream": "~2.3.6", + "setimmediate": "~1.0.4" + } + }, + "node_modules/unzipper/node_modules/bluebird": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", + "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==" + }, + "node_modules/unzipper/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/unzipper/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/unzipper/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/update-notifier": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-5.1.0.tgz", + "integrity": "sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw==", + "dependencies": { + "boxen": "^5.0.0", + "chalk": "^4.1.0", + "configstore": "^5.0.1", + "has-yarn": "^2.1.0", + "import-lazy": "^2.1.0", + "is-ci": "^2.0.0", + "is-installed-globally": "^0.4.0", + "is-npm": "^5.0.0", + "is-yarn-global": "^0.3.0", + "latest-version": "^5.1.0", + "pupa": "^2.1.1", + "semver": "^7.3.4", + "semver-diff": "^3.1.1", + "xdg-basedir": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/yeoman/update-notifier?sponsor=1" + } + }, + "node_modules/update-notifier/node_modules/boxen": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", + "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", + "dependencies": { + "ansi-align": "^3.0.0", + "camelcase": "^6.2.0", + "chalk": "^4.1.0", + "cli-boxes": "^2.2.1", + "string-width": "^4.2.2", + "type-fest": "^0.20.2", + "widest-line": "^3.1.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/update-notifier/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/update-notifier/node_modules/cli-boxes": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", + "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/update-notifier/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/update-notifier/node_modules/widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "dependencies": { + "string-width": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/url-join": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", + "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", + "dev": true + }, + "node_modules/url-loader": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz", + "integrity": "sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==", + "dependencies": { + "loader-utils": "^2.0.0", + "mime-types": "^2.1.27", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "file-loader": "*", + "webpack": "^4.0.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "file-loader": { + "optional": true + } + } + }, + "node_modules/url-loader/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dev": true, + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/use-composed-ref": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.3.0.tgz", + "integrity": "sha512-GLMG0Jc/jiKov/3Ulid1wbv3r54K9HlMW29IWcDFPEqFkSO2nS0MuefWgMJpeHQ9YJeXDL3ZUF+P3jdXlZX/cQ==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/use-isomorphic-layout-effect": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz", + "integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-latest": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/use-latest/-/use-latest-1.2.1.tgz", + "integrity": "sha512-xA+AVm/Wlg3e2P/JiItTziwS7FK92LWrDB0p+hgXloIMuVCeJJ8v6f0eeHyPZaJrM+usM1FkFfbNCrJGs8A/zw==", + "dependencies": { + "use-isomorphic-layout-effect": "^1.1.1" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/utila": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==" + }, + "node_modules/utility-types": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.10.0.tgz", + "integrity": "sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "node_modules/v8-to-istanbul": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz", + "integrity": "sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/v8-to-istanbul/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/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/validate-npm-package-name": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.0.tgz", + "integrity": "sha512-YuKoXDAhBYxY7SfOKxHBDoSyENFeW5VvIIQp2TGQuit8gpK6MnWaQelBKxso72DoxTZfZdcP3W90LqpSkgPzLQ==", + "dev": true, + "dependencies": { + "builtins": "^5.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/value-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", + "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==" + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vfile": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.2.1.tgz", + "integrity": "sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==", + "dependencies": { + "@types/unist": "^2.0.0", + "is-buffer": "^2.0.0", + "unist-util-stringify-position": "^2.0.0", + "vfile-message": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-location": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-3.2.0.tgz", + "integrity": "sha512-aLEIZKv/oxuCDZ8lkJGhuhztf/BW4M+iHdCwglA/eWc+vtuRFJj8EtgceYFX4LRjOhCAAiNHsKGssC6onJ+jbA==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.4.tgz", + "integrity": "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile/node_modules/is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "engines": { + "node": ">=4" + } + }, + "node_modules/vite": { + "version": "5.0.13", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.0.13.tgz", + "integrity": "sha512-/9ovhv2M2dGTuA+dY93B9trfyWMDRQw2jdVBhHNP6wr0oF34wG2i/N55801iZIpgUpnHDm4F/FabGQLyc+eOgg==", + "dev": true, + "dependencies": { + "esbuild": "^0.19.3", + "postcss": "^8.4.32", + "rollup": "^4.2.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.5.3.tgz", + "integrity": "sha512-axFo00qiCpU/JLd8N1gu9iEYL3xTbMbMrbe5nDp9GL0nb6gurIdZLkkFogZXWnE8Oyy5kfSLwNVIcVsnhE7lgQ==", + "dev": true, + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.4", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "vite": "^5.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vite/node_modules/rollup": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.17.2.tgz", + "integrity": "sha512-/9ClTJPByC0U4zNLowV1tMBe8yMEAxewtR3cUNX5BoEpGH3dQEWpJLr6CLp0fPdYRF/fzVOgvDb1zXuakwF5kQ==", + "dev": true, + "dependencies": { + "@types/estree": "1.0.5" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.17.2", + "@rollup/rollup-android-arm64": "4.17.2", + "@rollup/rollup-darwin-arm64": "4.17.2", + "@rollup/rollup-darwin-x64": "4.17.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.17.2", + "@rollup/rollup-linux-arm-musleabihf": "4.17.2", + "@rollup/rollup-linux-arm64-gnu": "4.17.2", + "@rollup/rollup-linux-arm64-musl": "4.17.2", + "@rollup/rollup-linux-powerpc64le-gnu": "4.17.2", + "@rollup/rollup-linux-riscv64-gnu": "4.17.2", + "@rollup/rollup-linux-s390x-gnu": "4.17.2", + "@rollup/rollup-linux-x64-gnu": "4.17.2", + "@rollup/rollup-linux-x64-musl": "4.17.2", + "@rollup/rollup-win32-arm64-msvc": "4.17.2", + "@rollup/rollup-win32-ia32-msvc": "4.17.2", + "@rollup/rollup-win32-x64-msvc": "4.17.2", + "fsevents": "~2.3.2" + } + }, + "node_modules/vitest": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.5.3.tgz", + "integrity": "sha512-2oM7nLXylw3mQlW6GXnRriw+7YvZFk/YNV8AxIC3Z3MfFbuziLGWP9GPxxu/7nRlXhqyxBikpamr+lEEj1sUEw==", + "dev": true, + "dependencies": { + "@vitest/expect": "1.5.3", + "@vitest/runner": "1.5.3", + "@vitest/snapshot": "1.5.3", + "@vitest/spy": "1.5.3", + "@vitest/utils": "1.5.3", + "acorn-walk": "^8.3.2", + "chai": "^4.3.10", + "debug": "^4.3.4", + "execa": "^8.0.1", + "local-pkg": "^0.5.0", + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "std-env": "^3.5.0", + "strip-literal": "^2.0.0", + "tinybench": "^2.5.1", + "tinypool": "^0.8.3", + "vite": "^5.0.0", + "vite-node": "1.5.3", + "why-is-node-running": "^2.2.2" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/node": "^18.0.0 || >=20.0.0", + "@vitest/browser": "1.5.3", + "@vitest/ui": "1.5.3", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/vitest/node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vitest/node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/vitest/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/vitest/node_modules/magic-string": { + "version": "0.30.10", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz", + "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + } + }, + "node_modules/vitest/node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vitest/node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "dev": true, + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vitest/node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vitest/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vitest/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/vitest/node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vscode": { + "name": "@codingame/monaco-vscode-api", + "version": "1.69.13", + "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-api/-/monaco-vscode-api-1.69.13.tgz", + "integrity": "sha512-7+dQbQ5O8mQhFyUcAiiJkCotNjZUzRxh4NMBKc/BSIFi0jG47bay+jP/+ngsmxHBapjs/xUAPaKSGNnf9WBmAA==", + "peerDependencies": { + "monaco-editor": "~0.34.0", + "vscode-oniguruma": "^1.6.2", + "vscode-textmate": "^7.0.1" + } + }, + "node_modules/vscode-jsonrpc": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.0.2.tgz", + "integrity": "sha512-RY7HwI/ydoC1Wwg4gJ3y6LpU9FJRZAUnTYMXthqhFXXu77ErDd/xkREpGuk4MyYkk4a+XDWAMqe0S3KkelYQEQ==", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vscode-languageclient": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-9.0.1.tgz", + "integrity": "sha512-JZiimVdvimEuHh5olxhxkht09m3JzUGwggb5eRUkzzJhZ2KjCN0nh55VfiED9oez9DyF8/fz1g1iBV3h+0Z2EA==", + "dependencies": { + "minimatch": "^5.1.0", + "semver": "^7.3.7", + "vscode-languageserver-protocol": "3.17.5" + }, + "engines": { + "vscode": "^1.82.0" + } + }, + "node_modules/vscode-languageclient/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/vscode-languageclient/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/vscode-languageclient/node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/vscode-languageclient/node_modules/vscode-jsonrpc": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", + "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vscode-languageclient/node_modules/vscode-languageserver-protocol": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", + "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", + "dependencies": { + "vscode-jsonrpc": "8.2.0", + "vscode-languageserver-types": "3.17.5" + } + }, + "node_modules/vscode-languageclient/node_modules/vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==" + }, + "node_modules/vscode-languageserver": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz", + "integrity": "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==", + "dependencies": { + "vscode-languageserver-protocol": "3.17.5" + }, + "bin": { + "installServerIntoExtension": "bin/installServerIntoExtension" + } + }, + "node_modules/vscode-languageserver-protocol": { + "version": "3.17.2", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.2.tgz", + "integrity": "sha512-8kYisQ3z/SQ2kyjlNeQxbkkTNmVFoQCqkmGrzLH6A9ecPlgTbp3wDTnUNqaUxYr4vlAcloxx8zwy7G5WdguYNg==", + "dependencies": { + "vscode-jsonrpc": "8.0.2", + "vscode-languageserver-types": "3.17.2" + } + }, + "node_modules/vscode-languageserver-textdocument": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.11.tgz", + "integrity": "sha512-X+8T3GoiwTVlJbicx/sIAF+yuJAqz8VvwJyoMVhwEMoEKE/fkDmrqUgDMyBECcM2A2frVZIUj5HI/ErRXCfOeA==" + }, + "node_modules/vscode-languageserver-types": { + "version": "3.17.2", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.2.tgz", + "integrity": "sha512-zHhCWatviizPIq9B7Vh9uvrH6x3sK8itC84HkamnBWoDFJtzBf7SWlpLCZUit72b3os45h6RWQNC9xHRDF8dRA==" + }, + "node_modules/vscode-languageserver/node_modules/vscode-jsonrpc": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", + "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vscode-languageserver/node_modules/vscode-languageserver-protocol": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", + "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", + "dependencies": { + "vscode-jsonrpc": "8.2.0", + "vscode-languageserver-types": "3.17.5" + } + }, + "node_modules/vscode-languageserver/node_modules/vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==" + }, + "node_modules/vscode-uri": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", + "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==" + }, + "node_modules/w3c-xmlserializer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", + "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==", + "dev": true, + "dependencies": { + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/wait-on": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-6.0.1.tgz", + "integrity": "sha512-zht+KASY3usTY5u2LgaNqn/Cd8MukxLGjdcZxT2ns5QzDmTFc4XoWBgC+C/na+sMRZTuVygQoMYwdcVjHnYIVw==", + "dependencies": { + "axios": "^0.25.0", + "joi": "^17.6.0", + "lodash": "^4.17.21", + "minimist": "^1.2.5", + "rxjs": "^7.5.4" + }, + "bin": { + "wait-on": "bin/wait-on" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/wait-on/node_modules/axios": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.25.0.tgz", + "integrity": "sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==", + "dependencies": { + "follow-redirects": "^1.14.7" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/watchpack": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dependencies": { + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/web-namespaces": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-1.1.4.tgz", + "integrity": "sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/web-worker": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.2.0.tgz", + "integrity": "sha512-PgF341avzqyx60neE9DD+XS26MMNMoUQRz9NOZwW32nPQrF6p77f1htcnjBSEV8BGMKZ16choqUG4hyI0Hx7mA==" + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/webpack": { + "version": "5.90.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.90.0.tgz", + "integrity": "sha512-bdmyXRCXeeNIePv6R6tGPyy20aUobw4Zy8r0LUS2EWO+U+Ke/gYDgsCh7bl5rB6jPpr4r0SZa6dPxBxLooDT3w==", + "dependencies": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^1.0.5", + "@webassemblyjs/ast": "^1.11.5", + "@webassemblyjs/wasm-edit": "^1.11.5", + "@webassemblyjs/wasm-parser": "^1.11.5", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.9.0", + "browserslist": "^4.21.10", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.15.0", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.2.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.10", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-bundle-analyzer": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.8.0.tgz", + "integrity": "sha512-ZzoSBePshOKhr+hd8u6oCkZVwpVaXgpw23ScGLFpR6SjYI7+7iIWYarjN6OEYOfRt8o7ZyZZQk0DuMizJ+LEIg==", + "dependencies": { + "@discoveryjs/json-ext": "0.5.7", + "acorn": "^8.0.4", + "acorn-walk": "^8.0.0", + "chalk": "^4.1.0", + "commander": "^7.2.0", + "gzip-size": "^6.0.0", + "lodash": "^4.17.20", + "opener": "^1.5.2", + "sirv": "^1.0.7", + "ws": "^7.3.1" + }, + "bin": { + "webpack-bundle-analyzer": "lib/bin/analyzer.js" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/webpack-bundle-analyzer/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/webpack-bundle-analyzer/node_modules/ws": { + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/webpack-dev-middleware": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz", + "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==", + "dependencies": { + "colorette": "^2.0.10", + "memfs": "^3.4.3", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/webpack-dev-middleware/node_modules/ajv": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz", + "integrity": "sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack-dev-middleware/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/webpack-dev-middleware/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/webpack-dev-middleware/node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-dev-server": { + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.11.1.tgz", + "integrity": "sha512-lILVz9tAUy1zGFwieuaQtYiadImb5M3d+H+L1zDYalYoDl0cksAB1UNyuE5MMWJrG6zR1tXkCP2fitl7yoUJiw==", + "dependencies": { + "@types/bonjour": "^3.5.9", + "@types/connect-history-api-fallback": "^1.3.5", + "@types/express": "^4.17.13", + "@types/serve-index": "^1.9.1", + "@types/serve-static": "^1.13.10", + "@types/sockjs": "^0.3.33", + "@types/ws": "^8.5.1", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.0.11", + "chokidar": "^3.5.3", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^2.0.0", + "default-gateway": "^6.0.3", + "express": "^4.17.3", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^2.0.3", + "ipaddr.js": "^2.0.1", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "rimraf": "^3.0.2", + "schema-utils": "^4.0.0", + "selfsigned": "^2.1.1", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^5.3.1", + "ws": "^8.4.2" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.37.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server/node_modules/ajv": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz", + "integrity": "sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack-dev-server/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/webpack-dev-server/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/webpack-dev-server/node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-merge": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", + "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", + "dependencies": { + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/webpack-node-externals": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-3.0.0.tgz", + "integrity": "sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack-subresource-integrity": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-5.1.0.tgz", + "integrity": "sha512-sacXoX+xd8r4WKsy9MvH/q/vBtEHr86cpImXwyg74pFIpERKt6FmB8cXpeuh0ZLgclOlHI4Wcll7+R5L02xk9Q==", + "dev": true, + "dependencies": { + "typed-assert": "^1.0.8" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "html-webpack-plugin": ">= 5.0.0-beta.1 < 6", + "webpack": "^5.12.0" + }, + "peerDependenciesMeta": { + "html-webpack-plugin": { + "optional": true + } + } + }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpackbar": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/webpackbar/-/webpackbar-5.0.2.tgz", + "integrity": "sha512-BmFJo7veBDgQzfWXl/wwYXr/VFus0614qZ8i9znqcl9fnEdiVkdbi0TedLQ6xAK92HZHDJ0QmyQ0fmuZPAgCYQ==", + "dependencies": { + "chalk": "^4.1.0", + "consola": "^2.15.3", + "pretty-time": "^1.1.0", + "std-env": "^3.0.1" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "webpack": "3 || 4 || 5" + } + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/whatwg-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "dev": true, + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "dev": true, + "dependencies": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", + "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/why-is-node-running": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.2.2.tgz", + "integrity": "sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==", + "dev": true, + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/widest-line": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", + "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==", + "dependencies": { + "string-width": "^5.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/widest-line/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/widest-line/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==" + }, + "node_modules/widest-line/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==", + "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/widest-line/node_modules/strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/wildcard": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", + "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==" + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xdg-basedir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/xml-js": { + "version": "1.6.11", + "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz", + "integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==", + "dependencies": { + "sax": "^1.2.4" + }, + "bin": { + "xml-js": "bin/cli.js" + } + }, + "node_modules/xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "dev": true, + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" + }, + "node_modules/xmlcreate": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", + "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==" + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "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": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/yargs": { + "version": "17.6.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", + "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==", + "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/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/yazl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yazl/-/yazl-2.5.1.tgz", + "integrity": "sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==", + "dev": true, + "dependencies": { + "buffer-crc32": "~0.2.3" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "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==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zip-stream": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz", + "integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==", + "dependencies": { + "archiver-utils": "^3.0.4", + "compress-commons": "^4.1.2", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/zip-stream/node_modules/archiver-utils": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-3.0.4.tgz", + "integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==", + "dependencies": { + "glob": "^7.2.3", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/zwitch": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-1.0.5.tgz", + "integrity": "sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + } + }, + "dependencies": { + "@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true + }, + "@adobe/css-tools": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.3.tgz", + "integrity": "sha512-rE0Pygv0sEZ4vBWHlAgJLGDU7Pm8xoO6p3wsEceb7GYAjScrOHpEo8KK/eVkAcnSM+slAEtXjA2JpdjLp4fJQQ==", + "dev": true + }, + "@algolia/autocomplete-core": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.9.2.tgz", + "integrity": "sha512-hkG80c9kx9ClVAEcUJbTd2ziVC713x9Bji9Ty4XJfKXlxlsx3iXsoNhAwfeR4ulzIUg7OE5gez0UU1zVDdG7kg==", + "requires": { + "@algolia/autocomplete-plugin-algolia-insights": "1.9.2", + "@algolia/autocomplete-shared": "1.9.2" + } + }, + "@algolia/autocomplete-plugin-algolia-insights": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.9.2.tgz", + "integrity": "sha512-2LVsf4W66hVHQ3Ua/8k15oPlxjELCztbAkQm/hP42Sw+GLkHAdY1vaVRYziaWq64+Oljfg6FKkZHCdgXH+CGIA==", + "requires": { + "@algolia/autocomplete-shared": "1.9.2" + } + }, + "@algolia/autocomplete-preset-algolia": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.9.2.tgz", + "integrity": "sha512-pqgIm2GNqtCT59Y1ICctIPrYTi34+wNPiNWEclD/yDzp5uDUUsyGe5XrUjCNyQRTKonAlmYxoaEHOn8FWgmBHA==", + "requires": { + "@algolia/autocomplete-shared": "1.9.2" + } + }, + "@algolia/autocomplete-shared": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.9.2.tgz", + "integrity": "sha512-XxX6YDn+7LG+SmdpXEOnj7fc3TjiVpQ0CbGhjLwrd2tYr6LVY2D4Iiu/iuYJ4shvVDWWnpwArSk0uIWC/8OPUA==" + }, + "@algolia/cache-browser-local-storage": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.17.2.tgz", + "integrity": "sha512-ZkVN7K/JE+qMQbpR6h3gQOGR6yCJpmucSBCmH5YDxnrYbp2CbrVCu0Nr+FGVoWzMJNznj1waShkfQ9awERulLw==", + "requires": { + "@algolia/cache-common": "4.17.2" + } + }, + "@algolia/cache-common": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.17.2.tgz", + "integrity": "sha512-fojbhYIS8ovfYs6hwZpy1O4mBfVRxNgAaZRqsdVQd54hU4MxYDYFCxagYX28lOBz7btcDHld6BMoWXvjzkx6iQ==" + }, + "@algolia/cache-in-memory": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.17.2.tgz", + "integrity": "sha512-UYQcMzPurNi+cPYkuPemTZkjKAjdgAS1hagC5irujKbrYnN4yscK4TkOI5tX+O8/KegtJt3kOK07OIrJ2QDAAw==", + "requires": { + "@algolia/cache-common": "4.17.2" + } + }, + "@algolia/client-account": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.17.2.tgz", + "integrity": "sha512-doSk89pBPDpDyKJSHFADIGa2XSGrBCj3QwPvqtRJXDADpN+OjW+eTR8r4hEs/7X4GGfjfAOAES8JgDx+fZntYw==", + "requires": { + "@algolia/client-common": "4.17.2", + "@algolia/client-search": "4.17.2", + "@algolia/transporter": "4.17.2" + } + }, + "@algolia/client-analytics": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.17.2.tgz", + "integrity": "sha512-V+DcXbOtD/hKwAR3qGQrtlrJ3q2f9OKfx843q744o4m3xHv5ueCAvGXB1znPsdaUrVDNAImcgEgqwI9x7EJbDw==", + "requires": { + "@algolia/client-common": "4.17.2", + "@algolia/client-search": "4.17.2", + "@algolia/requester-common": "4.17.2", + "@algolia/transporter": "4.17.2" + } + }, + "@algolia/client-common": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.17.2.tgz", + "integrity": "sha512-gKBUnjxi0ukJYIJxVREYGt1Dmj1B3RBYbfGWi0dIPp1BC1VvQm+BOuNwsIwmq/x3MPO+sGuK978eKiP3tZDvag==", + "requires": { + "@algolia/requester-common": "4.17.2", + "@algolia/transporter": "4.17.2" + } + }, + "@algolia/client-personalization": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.17.2.tgz", + "integrity": "sha512-wc4UgOWxSYWz5wpuelNmlt895jA9twjZWM2ms17Ws8qCvBHF7OVGdMGgbysPB8790YnfvvDnSsWOv3CEj26Eow==", + "requires": { + "@algolia/client-common": "4.17.2", + "@algolia/requester-common": "4.17.2", + "@algolia/transporter": "4.17.2" + } + }, + "@algolia/client-search": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.17.2.tgz", + "integrity": "sha512-FUjIs+gRe0upJC++uVs4sdxMw15JxfkT86Gr/kqVwi9kcqaZhXntSbW/Fw959bIYXczjmeVQsilYvBWW4YvSZA==", + "requires": { + "@algolia/client-common": "4.17.2", + "@algolia/requester-common": "4.17.2", + "@algolia/transporter": "4.17.2" + } + }, + "@algolia/events": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@algolia/events/-/events-4.0.1.tgz", + "integrity": "sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ==" + }, + "@algolia/logger-common": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.17.2.tgz", + "integrity": "sha512-EfXuweUE+1HiSMsQidaDWA5Lv4NnStYIlh7PO5pLkI+sdhbMX0e5AO5nUAMIFM1VkEANes70RA8fzhP6OqCqQQ==" + }, + "@algolia/logger-console": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.17.2.tgz", + "integrity": "sha512-JuG8HGVlJ+l/UEDK4h2Y8q/IEmRjQz1J0aS9tf6GPNbGYiSvMr1DDdZ+hqV3bb1XE6wU8Ypex56HisWMSpnG0A==", + "requires": { + "@algolia/logger-common": "4.17.2" + } + }, + "@algolia/requester-browser-xhr": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.17.2.tgz", + "integrity": "sha512-FKI2lYWwksALfRt2OETFmGb5+P7WVc4py2Ai3H7k8FSfTLwVvs9WVVmtlx6oANQ8RFEK4B85h8DQJTJ29TDfmA==", + "requires": { + "@algolia/requester-common": "4.17.2" + } + }, + "@algolia/requester-common": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.17.2.tgz", + "integrity": "sha512-Rfim23ztAhYpE9qm+KCfCRo+YLJCjiiTG+IpDdzUjMpYPhUtirQT0A35YEd/gKn86YNyydxS9w8iRSjwKh+L0A==" + }, + "@algolia/requester-node-http": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.17.2.tgz", + "integrity": "sha512-E0b0kyCDMvUIhQmDNd/mH4fsKJdEEX6PkMKrYJjzm6moo+rP22tqpq4Rfe7DZD8OB6/LsDD3zs3Kvd+L+M5wwQ==", + "requires": { + "@algolia/requester-common": "4.17.2" + } + }, + "@algolia/transporter": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.17.2.tgz", + "integrity": "sha512-m8pXlz5OnNzjD1rcw+duCN4jG4yEzkJBsvKYMoN22Oq6rQwy1AY5muZ+IQUs4dL+A364CYkRMLRWhvXpCZ1x+g==", + "requires": { + "@algolia/cache-common": "4.17.2", + "@algolia/logger-common": "4.17.2", + "@algolia/requester-common": "4.17.2" + } + }, + "@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "requires": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "@babel/code-frame": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", + "requires": { + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/compat-data": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", + "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==" + }, + "@babel/core": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.9.tgz", + "integrity": "sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw==", + "requires": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.23.9", + "@babel/parser": "^7.23.9", + "@babel/template": "^7.23.9", + "@babel/traverse": "^7.23.9", + "@babel/types": "^7.23.9", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "dependencies": { + "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==" + }, + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + } + } + }, + "@babel/eslint-parser": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.19.1.tgz", + "integrity": "sha512-AqNf2QWt1rtu2/1rLswy6CDP7H9Oh3mMhk177Y67Rg8d7RD9WfOLLv8CGn6tisFvS2htm86yIe1yLF6I1UDaGQ==", + "dev": true, + "requires": { + "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", + "eslint-visitor-keys": "^2.1.0", + "semver": "^6.3.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@babel/generator": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", + "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", + "requires": { + "@babel/types": "^7.23.6", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", + "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz", + "integrity": "sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==", + "requires": { + "@babel/types": "^7.22.15" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", + "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", + "requires": { + "@babel/compat-data": "^7.23.5", + "@babel/helper-validator-option": "^7.23.5", + "browserslist": "^4.22.2", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "dependencies": { + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "requires": { + "yallist": "^3.0.2" + } + }, + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + } + } + }, + "@babel/helper-create-class-features-plugin": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.23.9.tgz", + "integrity": "sha512-B2L9neXTIyPQoXDm+NtovPvG6VOLWnaXu3BIeVDWwdKFgG30oNa6CqVGiJPDWQwIAK49t9gnQI9c6K6RzabiKw==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-member-expression-to-functions": "^7.23.0", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.20", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "semver": "^6.3.1" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + } + } + }, + "@babel/helper-create-regexp-features-plugin": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz", + "integrity": "sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "regexpu-core": "^5.3.1", + "semver": "^6.3.1" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + } + } + }, + "@babel/helper-define-polyfill-provider": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.5.0.tgz", + "integrity": "sha512-NovQquuQLAQ5HuyjCz7WQP9MjRj7dx++yspwiyUiGl9ZyadHRSql1HZh5ogRd8W8w6YM6EQ/NTB8rgjLt5W65Q==", + "requires": { + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2" + } + }, + "@babel/helper-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==" + }, + "@babel/helper-function-name": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "requires": { + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz", + "integrity": "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==", + "requires": { + "@babel/types": "^7.23.0" + } + }, + "@babel/helper-module-imports": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", + "requires": { + "@babel/types": "^7.22.15" + } + }, + "@babel/helper-module-transforms": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", + "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", + "requires": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.20" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", + "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==" + }, + "@babel/helper-remap-async-to-generator": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz", + "integrity": "sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-wrap-function": "^7.22.20" + } + }, + "@babel/helper-replace-supers": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz", + "integrity": "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==", + "requires": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-member-expression-to-functions": "^7.22.15", + "@babel/helper-optimise-call-expression": "^7.22.5" + } + }, + "@babel/helper-simple-access": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", + "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-string-parser": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz", + "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==" + }, + "@babel/helper-validator-identifier": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz", + "integrity": "sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==" + }, + "@babel/helper-validator-option": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==" + }, + "@babel/helper-wrap-function": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.20.tgz", + "integrity": "sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw==", + "requires": { + "@babel/helper-function-name": "^7.22.5", + "@babel/template": "^7.22.15", + "@babel/types": "^7.22.19" + } + }, + "@babel/helpers": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.9.tgz", + "integrity": "sha512-87ICKgU5t5SzOT7sBMfCOZQ2rHjRU+Pcb9BoILMYz600W6DkVRLFBPwQ18gwUVvggqXivaUakpnxWQGbpywbBQ==", + "requires": { + "@babel/template": "^7.23.9", + "@babel/traverse": "^7.23.9", + "@babel/types": "^7.23.9" + } + }, + "@babel/highlight": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", + "requires": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/parser": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.5.tgz", + "integrity": "sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==" + }, + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.23.3.tgz", + "integrity": "sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.23.3.tgz", + "integrity": "sha512-WwlxbfMNdVEpQjZmK5mhm7oSwD3dS6eU+Iwsi4Knl9wAletWem7kaRsGOG+8UEbRyqxY4SS5zvtfXwX+jMxUwQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/plugin-transform-optional-chaining": "^7.23.3" + } + }, + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.23.7.tgz", + "integrity": "sha512-LlRT7HgaifEpQA1ZgLVOIJZZFVPWN5iReq/7/JixwBtwcoeVGDBD53ZV28rrsLYOZs1Y/EHhA8N/Z6aazHR8cw==", + "requires": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-proposal-class-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", + "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-proposal-decorators": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.23.9.tgz", + "integrity": "sha512-hJhBCb0+NnTWybvWq2WpbCYDOcflSbx0t+BYP65e5R9GVnukiDTi+on5bFkk4p7QGuv190H6KfNiV9Knf/3cZA==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.23.9", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-decorators": "^7.23.3" + } + }, + "@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", + "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + } + }, + "@babel/plugin-proposal-numeric-separator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", + "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + } + }, + "@babel/plugin-proposal-optional-chaining": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.9.tgz", + "integrity": "sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + } + }, + "@babel/plugin-proposal-private-methods": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", + "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==" + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-decorators": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.23.3.tgz", + "integrity": "sha512-cf7Niq4/+/juY67E0PbgH0TDhLQ5J7zS8C/Q5FFx+DWyrRa9sUQdTXkjqKu8zGvuqr7vw1muKiukseihU+PJDA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-syntax-flow": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.18.6.tgz", + "integrity": "sha512-LUbR+KNTBWCUAqRG9ex5Gnzu2IOkt8jRJbHHXFT9q+L9zm7M/QQbEqXyw1n1pohYvOyWC8CjeyjrSaIwiYjK7A==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-syntax-import-assertions": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.23.3.tgz", + "integrity": "sha512-lPgDSU+SJLK3xmFDTV2ZRQAiM7UuUjGidwBywFavObCiZc1BeAAcMtHJKUya92hPHO+at63JJPLygilZard8jw==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-syntax-import-attributes": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.23.3.tgz", + "integrity": "sha512-pawnE0P9g10xgoP7yKr6CK63K2FMsTE+FZidZO/1PwRdzmAPVs+HS1mAURUsgaoxammTJvULUdIkEK0gOcU2tA==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-jsx": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz", + "integrity": "sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-typescript": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz", + "integrity": "sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-arrow-functions": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.23.3.tgz", + "integrity": "sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-async-generator-functions": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.9.tgz", + "integrity": "sha512-8Q3veQEDGe14dTYuwagbRtwxQDnytyg1JFu4/HwEMETeofocrB0U0ejBJIXoeG/t2oXZ8kzCyI0ZZfbT80VFNQ==", + "requires": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-remap-async-to-generator": "^7.22.20", + "@babel/plugin-syntax-async-generators": "^7.8.4" + } + }, + "@babel/plugin-transform-async-to-generator": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.23.3.tgz", + "integrity": "sha512-A7LFsKi4U4fomjqXJlZg/u0ft/n8/7n7lpffUP/ZULx/DtV9SGlNKZolHH6PE8Xl1ngCc0M11OaeZptXVkfKSw==", + "requires": { + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-remap-async-to-generator": "^7.22.20" + } + }, + "@babel/plugin-transform-block-scoped-functions": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.23.3.tgz", + "integrity": "sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-block-scoping": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.4.tgz", + "integrity": "sha512-0QqbP6B6HOh7/8iNR4CQU2Th/bbRtBp4KS9vcaZd1fZ0wSh5Fyssg0UCIHwxh+ka+pNDREbVLQnHCMHKZfPwfw==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-class-properties": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.23.3.tgz", + "integrity": "sha512-uM+AN8yCIjDPccsKGlw271xjJtGii+xQIF/uMPS8H15L12jZTsLfF4o5vNO7d/oUguOyfdikHGc/yi9ge4SGIg==", + "requires": { + "@babel/helper-create-class-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-class-static-block": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.23.4.tgz", + "integrity": "sha512-nsWu/1M+ggti1SOALj3hfx5FXzAY06fwPJsUZD4/A5e1bWi46VUIWtD+kOX6/IdhXGsXBWllLFDSnqSCdUNydQ==", + "requires": { + "@babel/helper-create-class-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + } + }, + "@babel/plugin-transform-classes": { + "version": "7.23.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.23.8.tgz", + "integrity": "sha512-yAYslGsY1bX6Knmg46RjiCiNSwJKv2IUC8qOdYKqMMr0491SXFhcHqOdRDeCRohOOIzwN/90C6mQ9qAKgrP7dg==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.20", + "@babel/helper-split-export-declaration": "^7.22.6", + "globals": "^11.1.0" + } + }, + "@babel/plugin-transform-computed-properties": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.23.3.tgz", + "integrity": "sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/template": "^7.22.15" + } + }, + "@babel/plugin-transform-destructuring": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.23.3.tgz", + "integrity": "sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-dotall-regex": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.23.3.tgz", + "integrity": "sha512-vgnFYDHAKzFaTVp+mneDsIEbnJ2Np/9ng9iviHw3P/KVcgONxpNULEW/51Z/BaFojG2GI2GwwXck5uV1+1NOYQ==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-duplicate-keys": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.23.3.tgz", + "integrity": "sha512-RrqQ+BQmU3Oyav3J+7/myfvRCq7Tbz+kKLLshUmMwNlDHExbGL7ARhajvoBJEvc+fCguPPu887N+3RRXBVKZUA==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-dynamic-import": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.23.4.tgz", + "integrity": "sha512-V6jIbLhdJK86MaLh4Jpghi8ho5fGzt3imHOBu/x0jlBaPYqDoWz4RDXjmMOfnh+JWNaQleEAByZLV0QzBT4YQQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + } + }, + "@babel/plugin-transform-exponentiation-operator": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.23.3.tgz", + "integrity": "sha512-5fhCsl1odX96u7ILKHBj4/Y8vipoqwsJMh4csSA8qFfxrZDEA4Ssku2DyNvMJSmZNOEBT750LfFPbtrnTP90BQ==", + "requires": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-export-namespace-from": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.23.4.tgz", + "integrity": "sha512-GzuSBcKkx62dGzZI1WVgTWvkkz84FZO5TC5T8dl/Tht/rAla6Dg/Mz9Yhypg+ezVACf/rgDuQt3kbWEv7LdUDQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + } + }, + "@babel/plugin-transform-flow-strip-types": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.19.0.tgz", + "integrity": "sha512-sgeMlNaQVbCSpgLSKP4ZZKfsJVnFnNQlUSk6gPYzR/q7tzCgQF2t8RBKAP6cKJeZdveei7Q7Jm527xepI8lNLg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/plugin-syntax-flow": "^7.18.6" + } + }, + "@babel/plugin-transform-for-of": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.23.6.tgz", + "integrity": "sha512-aYH4ytZ0qSuBbpfhuofbg/e96oQ7U2w1Aw/UQmKT+1l39uEhUPoFS3fHevDc1G0OvewyDudfMKY1OulczHzWIw==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" + } + }, + "@babel/plugin-transform-function-name": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.23.3.tgz", + "integrity": "sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw==", + "requires": { + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-json-strings": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.23.4.tgz", + "integrity": "sha512-81nTOqM1dMwZ/aRXQ59zVubN9wHGqk6UtqRK+/q+ciXmRy8fSolhGVvG09HHRGo4l6fr/c4ZhXUQH0uFW7PZbg==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-json-strings": "^7.8.3" + } + }, + "@babel/plugin-transform-literals": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.23.3.tgz", + "integrity": "sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-logical-assignment-operators": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.23.4.tgz", + "integrity": "sha512-Mc/ALf1rmZTP4JKKEhUwiORU+vcfarFVLfcFiolKUo6sewoxSEgl36ak5t+4WamRsNr6nzjZXQjM35WsU+9vbg==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + } + }, + "@babel/plugin-transform-member-expression-literals": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.23.3.tgz", + "integrity": "sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-modules-amd": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.23.3.tgz", + "integrity": "sha512-vJYQGxeKM4t8hYCKVBlZX/gtIY2I7mRGFNcm85sgXGMTBcoV3QdVtdpbcWEbzbfUIUZKwvgFT82mRvaQIebZzw==", + "requires": { + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-modules-commonjs": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.3.tgz", + "integrity": "sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==", + "requires": { + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-simple-access": "^7.22.5" + } + }, + "@babel/plugin-transform-modules-systemjs": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.9.tgz", + "integrity": "sha512-KDlPRM6sLo4o1FkiSlXoAa8edLXFsKKIda779fbLrvmeuc3itnjCtaO6RrtoaANsIJANj+Vk1zqbZIMhkCAHVw==", + "requires": { + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20" + } + }, + "@babel/plugin-transform-modules-umd": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.23.3.tgz", + "integrity": "sha512-zHsy9iXX2nIsCBFPud3jKn1IRPWg3Ing1qOZgeKV39m1ZgIdpJqvlWVeiHBZC6ITRG0MfskhYe9cLgntfSFPIg==", + "requires": { + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz", + "integrity": "sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-new-target": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.23.3.tgz", + "integrity": "sha512-YJ3xKqtJMAT5/TIZnpAR3I+K+WaDowYbN3xyxI8zxx/Gsypwf9B9h0VB+1Nh6ACAAPRS5NSRje0uVv5i79HYGQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.23.4.tgz", + "integrity": "sha512-jHE9EVVqHKAQx+VePv5LLGHjmHSJR76vawFPTdlxR/LVJPfOEGxREQwQfjuZEOPTwG92X3LINSh3M40Rv4zpVA==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + } + }, + "@babel/plugin-transform-numeric-separator": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.23.4.tgz", + "integrity": "sha512-mps6auzgwjRrwKEZA05cOwuDc9FAzoyFS4ZsG/8F43bTLf/TgkJg7QXOrPO1JO599iA3qgK9MXdMGOEC8O1h6Q==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + } + }, + "@babel/plugin-transform-object-rest-spread": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.23.4.tgz", + "integrity": "sha512-9x9K1YyeQVw0iOXJlIzwm8ltobIIv7j2iLyP2jIhEbqPRQ7ScNgwQufU2I0Gq11VjyG4gI4yMXt2VFags+1N3g==", + "requires": { + "@babel/compat-data": "^7.23.3", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.23.3" + } + }, + "@babel/plugin-transform-object-super": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.23.3.tgz", + "integrity": "sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.20" + } + }, + "@babel/plugin-transform-optional-catch-binding": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.23.4.tgz", + "integrity": "sha512-XIq8t0rJPHf6Wvmbn9nFxU6ao4c7WhghTR5WyV8SrJfUFzyxhCm4nhC+iAp3HFhbAKLfYpgzhJ6t4XCtVwqO5A==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + } + }, + "@babel/plugin-transform-optional-chaining": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.4.tgz", + "integrity": "sha512-ZU8y5zWOfjM5vZ+asjgAPwDaBjJzgufjES89Rs4Lpq63O300R/kOz30WCLo6BxxX6QVEilwSlpClnG5cZaikTA==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + } + }, + "@babel/plugin-transform-parameters": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.23.3.tgz", + "integrity": "sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-private-methods": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.23.3.tgz", + "integrity": "sha512-UzqRcRtWsDMTLrRWFvUBDwmw06tCQH9Rl1uAjfh6ijMSmGYQ+fpdB+cnqRC8EMh5tuuxSv0/TejGL+7vyj+50g==", + "requires": { + "@babel/helper-create-class-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-private-property-in-object": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.23.4.tgz", + "integrity": "sha512-9G3K1YqTq3F4Vt88Djx1UZ79PDyj+yKRnUy7cZGSMe+a7jkwD259uKKuUzQlPkGam7R+8RJwh5z4xO27fA1o2A==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + } + }, + "@babel/plugin-transform-property-literals": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.23.3.tgz", + "integrity": "sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-react-constant-elements": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.23.3.tgz", + "integrity": "sha512-zP0QKq/p6O42OL94udMgSfKXyse4RyJ0JqbQ34zDAONWjyrEsghYEyTSK5FIpmXmCpB55SHokL1cRRKHv8L2Qw==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-react-display-name": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.18.6.tgz", + "integrity": "sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-react-jsx": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.19.0.tgz", + "integrity": "sha512-UVEvX3tXie3Szm3emi1+G63jyw1w5IcMY0FSKM+CRnKRI5Mr1YbCNgsSTwoTwKphQEG9P+QqmuRFneJPZuHNhg==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/plugin-syntax-jsx": "^7.18.6", + "@babel/types": "^7.19.0" + } + }, + "@babel/plugin-transform-react-jsx-development": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.18.6.tgz", + "integrity": "sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA==", + "requires": { + "@babel/plugin-transform-react-jsx": "^7.18.6" + } + }, + "@babel/plugin-transform-react-pure-annotations": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.18.6.tgz", + "integrity": "sha512-I8VfEPg9r2TRDdvnHgPepTKvuRomzA8+u+nhY7qSI1fR2hRNebasZEETLyM5mAUr0Ku56OkXJ0I7NHJnO6cJiQ==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-regenerator": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.23.3.tgz", + "integrity": "sha512-KP+75h0KghBMcVpuKisx3XTu9Ncut8Q8TuvGO4IhY+9D5DFEckQefOuIsB/gQ2tG71lCke4NMrtIPS8pOj18BQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "regenerator-transform": "^0.15.2" + } + }, + "@babel/plugin-transform-reserved-words": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.23.3.tgz", + "integrity": "sha512-QnNTazY54YqgGxwIexMZva9gqbPa15t/x9VS+0fsEFWplwVpXYZivtgl43Z1vMpc1bdPP2PP8siFeVcnFvA3Cg==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-runtime": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.23.9.tgz", + "integrity": "sha512-A7clW3a0aSjm3ONU9o2HAILSegJCYlEZmOhmBRReVtIpY/Z/p7yIZ+wR41Z+UipwdGuqwtID/V/dOdZXjwi9gQ==", + "requires": { + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "babel-plugin-polyfill-corejs2": "^0.4.8", + "babel-plugin-polyfill-corejs3": "^0.9.0", + "babel-plugin-polyfill-regenerator": "^0.5.5", + "semver": "^6.3.1" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + } + } + }, + "@babel/plugin-transform-shorthand-properties": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.23.3.tgz", + "integrity": "sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-spread": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.23.3.tgz", + "integrity": "sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" + } + }, + "@babel/plugin-transform-sticky-regex": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.23.3.tgz", + "integrity": "sha512-HZOyN9g+rtvnOU3Yh7kSxXrKbzgrm5X4GncPY1QOquu7epga5MxKHVpYu2hvQnry/H+JjckSYRb93iNfsioAGg==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-template-literals": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.23.3.tgz", + "integrity": "sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-typeof-symbol": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.23.3.tgz", + "integrity": "sha512-4t15ViVnaFdrPC74be1gXBSMzXk3B4Us9lP7uLRQHTFpV5Dvt33pn+2MyyNxmN3VTTm3oTrZVMUmuw3oBnQ2oQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-typescript": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.23.6.tgz", + "integrity": "sha512-6cBG5mBvUu4VUD04OHKnYzbuHNP8huDsD3EDqqpIpsswTDoqHCjLoHb6+QgsV1WsT2nipRqCPgxD3LXnEO7XfA==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.23.6", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-typescript": "^7.23.3" + } + }, + "@babel/plugin-transform-unicode-escapes": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.23.3.tgz", + "integrity": "sha512-OMCUx/bU6ChE3r4+ZdylEqAjaQgHAgipgW8nsCfu5pGqDcFytVd91AwRvUJSBZDz0exPGgnjoqhgRYLRjFZc9Q==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-unicode-property-regex": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.23.3.tgz", + "integrity": "sha512-KcLIm+pDZkWZQAFJ9pdfmh89EwVfmNovFBcXko8szpBeF8z68kWIPeKlmSOkT9BXJxs2C0uk+5LxoxIv62MROA==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-unicode-regex": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.23.3.tgz", + "integrity": "sha512-wMHpNA4x2cIA32b/ci3AfwNgheiva2W0WUKWTK7vBHBhDKfPsc5cFGNWm69WBqpwd86u1qwZ9PWevKqm1A3yAw==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-unicode-sets-regex": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.23.3.tgz", + "integrity": "sha512-W7lliA/v9bNR83Qc3q1ip9CQMZ09CcHDbHfbLRDNuAhn1Mvkr1ZNF7hPmztMQvtTGVLJ9m8IZqWsTkXOml8dbw==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/preset-env": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.23.9.tgz", + "integrity": "sha512-3kBGTNBBk9DQiPoXYS0g0BYlwTQYUTifqgKTjxUwEUkduRT2QOa0FPGBJ+NROQhGyYO5BuTJwGvBnqKDykac6A==", + "requires": { + "@babel/compat-data": "^7.23.5", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-option": "^7.23.5", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.23.3", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.23.3", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.23.7", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-import-assertions": "^7.23.3", + "@babel/plugin-syntax-import-attributes": "^7.23.3", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.23.3", + "@babel/plugin-transform-async-generator-functions": "^7.23.9", + "@babel/plugin-transform-async-to-generator": "^7.23.3", + "@babel/plugin-transform-block-scoped-functions": "^7.23.3", + "@babel/plugin-transform-block-scoping": "^7.23.4", + "@babel/plugin-transform-class-properties": "^7.23.3", + "@babel/plugin-transform-class-static-block": "^7.23.4", + "@babel/plugin-transform-classes": "^7.23.8", + "@babel/plugin-transform-computed-properties": "^7.23.3", + "@babel/plugin-transform-destructuring": "^7.23.3", + "@babel/plugin-transform-dotall-regex": "^7.23.3", + "@babel/plugin-transform-duplicate-keys": "^7.23.3", + "@babel/plugin-transform-dynamic-import": "^7.23.4", + "@babel/plugin-transform-exponentiation-operator": "^7.23.3", + "@babel/plugin-transform-export-namespace-from": "^7.23.4", + "@babel/plugin-transform-for-of": "^7.23.6", + "@babel/plugin-transform-function-name": "^7.23.3", + "@babel/plugin-transform-json-strings": "^7.23.4", + "@babel/plugin-transform-literals": "^7.23.3", + "@babel/plugin-transform-logical-assignment-operators": "^7.23.4", + "@babel/plugin-transform-member-expression-literals": "^7.23.3", + "@babel/plugin-transform-modules-amd": "^7.23.3", + "@babel/plugin-transform-modules-commonjs": "^7.23.3", + "@babel/plugin-transform-modules-systemjs": "^7.23.9", + "@babel/plugin-transform-modules-umd": "^7.23.3", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", + "@babel/plugin-transform-new-target": "^7.23.3", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.23.4", + "@babel/plugin-transform-numeric-separator": "^7.23.4", + "@babel/plugin-transform-object-rest-spread": "^7.23.4", + "@babel/plugin-transform-object-super": "^7.23.3", + "@babel/plugin-transform-optional-catch-binding": "^7.23.4", + "@babel/plugin-transform-optional-chaining": "^7.23.4", + "@babel/plugin-transform-parameters": "^7.23.3", + "@babel/plugin-transform-private-methods": "^7.23.3", + "@babel/plugin-transform-private-property-in-object": "^7.23.4", + "@babel/plugin-transform-property-literals": "^7.23.3", + "@babel/plugin-transform-regenerator": "^7.23.3", + "@babel/plugin-transform-reserved-words": "^7.23.3", + "@babel/plugin-transform-shorthand-properties": "^7.23.3", + "@babel/plugin-transform-spread": "^7.23.3", + "@babel/plugin-transform-sticky-regex": "^7.23.3", + "@babel/plugin-transform-template-literals": "^7.23.3", + "@babel/plugin-transform-typeof-symbol": "^7.23.3", + "@babel/plugin-transform-unicode-escapes": "^7.23.3", + "@babel/plugin-transform-unicode-property-regex": "^7.23.3", + "@babel/plugin-transform-unicode-regex": "^7.23.3", + "@babel/plugin-transform-unicode-sets-regex": "^7.23.3", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "babel-plugin-polyfill-corejs2": "^0.4.8", + "babel-plugin-polyfill-corejs3": "^0.9.0", + "babel-plugin-polyfill-regenerator": "^0.5.5", + "core-js-compat": "^3.31.0", + "semver": "^6.3.1" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + } + } + }, + "@babel/preset-modules": { + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + } + }, + "@babel/preset-react": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.18.6.tgz", + "integrity": "sha512-zXr6atUmyYdiWRVLOZahakYmOBHtWc2WGCkP8PYTgZi0iJXDY2CN180TdrIW4OGOAdLc7TifzDIvtx6izaRIzg==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-transform-react-display-name": "^7.18.6", + "@babel/plugin-transform-react-jsx": "^7.18.6", + "@babel/plugin-transform-react-jsx-development": "^7.18.6", + "@babel/plugin-transform-react-pure-annotations": "^7.18.6" + } + }, + "@babel/preset-typescript": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.23.3.tgz", + "integrity": "sha512-17oIGVlqz6CchO9RFYn5U6ZpWRZIngayYCtrPRSgANSwC2V1Jb+iP74nVxzzXJte8b8BYxrL1yY96xfhTBrNNQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-option": "^7.22.15", + "@babel/plugin-syntax-jsx": "^7.23.3", + "@babel/plugin-transform-modules-commonjs": "^7.23.3", + "@babel/plugin-transform-typescript": "^7.23.3" + } + }, + "@babel/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" + }, + "@babel/runtime": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz", + "integrity": "sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==", + "requires": { + "regenerator-runtime": "^0.14.0" + } + }, + "@babel/runtime-corejs3": { + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.20.6.tgz", + "integrity": "sha512-tqeujPiuEfcH067mx+7otTQWROVMKHXEaOQcAeNV5dDdbPWvPcFA8/W9LXw2NfjNmOetqLl03dfnG2WALPlsRQ==", + "requires": { + "core-js-pure": "^3.25.1", + "regenerator-runtime": "^0.13.11" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + } + } + }, + "@babel/template": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.23.9.tgz", + "integrity": "sha512-+xrD2BWLpvHKNmX2QbpdpsBaWnRxahMwJjO+KZk2JOElj5nSmKezyS1B4u+QbHMTX69t4ukm6hh9lsYQ7GHCKA==", + "requires": { + "@babel/code-frame": "^7.23.5", + "@babel/parser": "^7.23.9", + "@babel/types": "^7.23.9" + } + }, + "@babel/traverse": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.9.tgz", + "integrity": "sha512-I/4UJ9vs90OkBtY6iiiTORVMyIhJ4kAVmsKo9KFc8UOxMeUfi2hvtIBsET5u9GizXE6/GFSuKCTNfgCswuEjRg==", + "requires": { + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.9", + "@babel/types": "^7.23.9", + "debug": "^4.3.1", + "globals": "^11.1.0" + } + }, + "@babel/types": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.5.tgz", + "integrity": "sha512-6mQNsaLeXTw0nxYUYu+NSa4Hx4BlF1x1x8/PMFbiR+GBSr+2DkECc69b8hgy2frEodNcvPffeH8YfWd3LI6jhQ==", + "requires": { + "@babel/helper-string-parser": "^7.24.1", + "@babel/helper-validator-identifier": "^7.24.5", + "to-fast-properties": "^2.0.0" + } + }, + "@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "@braintree/sanitize-url": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-6.0.2.tgz", + "integrity": "sha512-Tbsj02wXCbqGmzdnXNk0SOF19ChhRU70BsroIi4Pm6Ehp56in6vch94mfbdQ17DozxkL3BAVjbZ4Qc1a0HFRAg==" + }, + "@chevrotain/cst-dts-gen": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-11.0.3.tgz", + "integrity": "sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ==", + "requires": { + "@chevrotain/gast": "11.0.3", + "@chevrotain/types": "11.0.3", + "lodash-es": "4.17.21" + } + }, + "@chevrotain/gast": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-11.0.3.tgz", + "integrity": "sha512-+qNfcoNk70PyS/uxmj3li5NiECO+2YKZZQMbmjTqRI3Qchu8Hig/Q9vgkHpI3alNjr7M+a2St5pw5w5F6NL5/Q==", + "requires": { + "@chevrotain/types": "11.0.3", + "lodash-es": "4.17.21" + } + }, + "@chevrotain/regexp-to-ast": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/regexp-to-ast/-/regexp-to-ast-11.0.3.tgz", + "integrity": "sha512-1fMHaBZxLFvWI067AVbGJav1eRY7N8DDvYCTwGBiE/ytKBgP8azTdgyrKyWZ9Mfh09eHWb5PgTSO8wi7U824RA==" + }, + "@chevrotain/types": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-11.0.3.tgz", + "integrity": "sha512-gsiM3G8b58kZC2HaWR50gu6Y1440cHiJ+i3JUvcp/35JchYejb2+5MVeJK0iKThYpAa/P2PYFV4hoi44HD+aHQ==" + }, + "@chevrotain/utils": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-11.0.3.tgz", + "integrity": "sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ==" + }, + "@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "optional": true + }, + "@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "dependencies": { + "@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + } + } + }, + "@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==" + }, + "@docsearch/css": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.5.0.tgz", + "integrity": "sha512-Ob5FQLubplcBNihAVtriR59FRBeP8u69F6mu4L4yIr60KfsPc10bOV0DoPErJw0zF9IBN2cNLW9qdmt8zWPxyg==" + }, + "@docsearch/react": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.5.0.tgz", + "integrity": "sha512-3IG8mmSMzSHNGy2S1VuPyYU9tFCxFpj5Ov8SYwsSHM4yMvFsaO9oFxXocA5lSenliIELhuOuS5+BdxHa/Qlf2A==", + "requires": { + "@algolia/autocomplete-core": "1.9.2", + "@algolia/autocomplete-preset-algolia": "1.9.2", + "@docsearch/css": "3.5.0", + "algoliasearch": "^4.0.0" + } + }, + "@docusaurus/core": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-2.4.1.tgz", + "integrity": "sha512-SNsY7PshK3Ri7vtsLXVeAJGS50nJN3RgF836zkyUfAD01Fq+sAk5EwWgLw+nnm5KVNGDu7PRR2kRGDsWvqpo0g==", + "requires": { + "@babel/core": "^7.18.6", + "@babel/generator": "^7.18.7", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-transform-runtime": "^7.18.6", + "@babel/preset-env": "^7.18.6", + "@babel/preset-react": "^7.18.6", + "@babel/preset-typescript": "^7.18.6", + "@babel/runtime": "^7.18.6", + "@babel/runtime-corejs3": "^7.18.6", + "@babel/traverse": "^7.18.8", + "@docusaurus/cssnano-preset": "2.4.1", + "@docusaurus/logger": "2.4.1", + "@docusaurus/mdx-loader": "2.4.1", + "@docusaurus/react-loadable": "5.5.2", + "@docusaurus/utils": "2.4.1", + "@docusaurus/utils-common": "2.4.1", + "@docusaurus/utils-validation": "2.4.1", + "@slorber/static-site-generator-webpack-plugin": "^4.0.7", + "@svgr/webpack": "^6.2.1", + "autoprefixer": "^10.4.7", + "babel-loader": "^8.2.5", + "babel-plugin-dynamic-import-node": "^2.3.3", + "boxen": "^6.2.1", + "chalk": "^4.1.2", + "chokidar": "^3.5.3", + "clean-css": "^5.3.0", + "cli-table3": "^0.6.2", + "combine-promises": "^1.1.0", + "commander": "^5.1.0", + "copy-webpack-plugin": "^11.0.0", + "core-js": "^3.23.3", + "css-loader": "^6.7.1", + "css-minimizer-webpack-plugin": "^4.0.0", + "cssnano": "^5.1.12", + "del": "^6.1.1", + "detect-port": "^1.3.0", + "escape-html": "^1.0.3", + "eta": "^2.0.0", + "file-loader": "^6.2.0", + "fs-extra": "^10.1.0", + "html-minifier-terser": "^6.1.0", + "html-tags": "^3.2.0", + "html-webpack-plugin": "^5.5.0", + "import-fresh": "^3.3.0", + "leven": "^3.1.0", + "lodash": "^4.17.21", + "mini-css-extract-plugin": "^2.6.1", + "postcss": "^8.4.14", + "postcss-loader": "^7.0.0", + "prompts": "^2.4.2", + "react-dev-utils": "^12.0.1", + "react-helmet-async": "^1.3.0", + "react-loadable": "npm:@docusaurus/react-loadable@5.5.2", + "react-loadable-ssr-addon-v5-slorber": "^1.0.1", + "react-router": "^5.3.3", + "react-router-config": "^5.1.1", + "react-router-dom": "^5.3.3", + "rtl-detect": "^1.0.4", + "semver": "^7.3.7", + "serve-handler": "^6.1.3", + "shelljs": "^0.8.5", + "terser-webpack-plugin": "^5.3.3", + "tslib": "^2.4.0", + "update-notifier": "^5.1.0", + "url-loader": "^4.1.1", + "wait-on": "^6.0.1", + "webpack": "^5.73.0", + "webpack-bundle-analyzer": "^4.5.0", + "webpack-dev-server": "^4.9.3", + "webpack-merge": "^5.8.0", + "webpackbar": "^5.0.2" + }, + "dependencies": { + "ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==" + }, + "copy-webpack-plugin": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz", + "integrity": "sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==", + "requires": { + "fast-glob": "^3.2.11", + "glob-parent": "^6.0.1", + "globby": "^13.1.1", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0" + }, + "dependencies": { + "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==", + "requires": { + "is-glob": "^4.0.3" + } + } + } + }, + "css-minimizer-webpack-plugin": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-4.2.2.tgz", + "integrity": "sha512-s3Of/4jKfw1Hj9CxEO1E5oXhQAxlayuHO2y/ML+C6I9sQ7FdzfEV6QgMLN3vI+qFsjJGIAFLKtQK7t8BOXAIyA==", + "requires": { + "cssnano": "^5.1.8", + "jest-worker": "^29.1.2", + "postcss": "^8.4.17", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0", + "source-map": "^0.6.1" + } + }, + "fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "globby": { + "version": "13.1.3", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.3.tgz", + "integrity": "sha512-8krCNHXvlCgHDpegPzleMq07yMYTO2sXKASmZmquEYWEmCx6J5UTRbp5RwMJkTJGtcQ44YpiUYUiN0b9mzy8Bw==", + "requires": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.11", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^4.0.0" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "mini-css-extract-plugin": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.2.tgz", + "integrity": "sha512-EdlUizq13o0Pd+uCp+WO/JpkLvHRVGt97RqfeGhXqAcorYo1ypJSpkV+WDT0vY/kmh/p7wRdJNJtuyK540PXDw==", + "requires": { + "schema-utils": "^4.0.0" + } + }, + "postcss-loader": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.0.2.tgz", + "integrity": "sha512-fUJzV/QH7NXUAqV8dWJ9Lg4aTkDCezpTS5HgJ2DvqznexTbSTxgi/dTECvTZ15BwKTtk8G/bqI/QTu2HPd3ZCg==", + "requires": { + "cosmiconfig": "^7.0.0", + "klona": "^2.0.5", + "semver": "^7.3.8" + } + }, + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + }, + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==" + } + } + }, + "@docusaurus/cssnano-preset": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-2.4.1.tgz", + "integrity": "sha512-ka+vqXwtcW1NbXxWsh6yA1Ckii1klY9E53cJ4O9J09nkMBgrNX3iEFED1fWdv8wf4mJjvGi5RLZ2p9hJNjsLyQ==", + "requires": { + "cssnano-preset-advanced": "^5.3.8", + "postcss": "^8.4.14", + "postcss-sort-media-queries": "^4.2.1", + "tslib": "^2.4.0" + } + }, + "@docusaurus/logger": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-2.4.1.tgz", + "integrity": "sha512-5h5ysIIWYIDHyTVd8BjheZmQZmEgWDR54aQ1BX9pjFfpyzFo5puKXKYrYJXbjEHGyVhEzmB9UXwbxGfaZhOjcg==", + "requires": { + "chalk": "^4.1.2", + "tslib": "^2.4.0" + } + }, + "@docusaurus/mdx-loader": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-2.4.1.tgz", + "integrity": "sha512-4KhUhEavteIAmbBj7LVFnrVYDiU51H5YWW1zY6SmBSte/YLhDutztLTBE0PQl1Grux1jzUJeaSvAzHpTn6JJDQ==", + "requires": { + "@babel/parser": "^7.18.8", + "@babel/traverse": "^7.18.8", + "@docusaurus/logger": "2.4.1", + "@docusaurus/utils": "2.4.1", + "@mdx-js/mdx": "^1.6.22", + "escape-html": "^1.0.3", + "file-loader": "^6.2.0", + "fs-extra": "^10.1.0", + "image-size": "^1.0.1", + "mdast-util-to-string": "^2.0.0", + "remark-emoji": "^2.2.0", + "stringify-object": "^3.3.0", + "tslib": "^2.4.0", + "unified": "^9.2.2", + "unist-util-visit": "^2.0.3", + "url-loader": "^4.1.1", + "webpack": "^5.73.0" + }, + "dependencies": { + "image-size": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.0.2.tgz", + "integrity": "sha512-xfOoWjceHntRb3qFCrh5ZFORYH8XCdYpASltMhZ/Q0KZiOwjdE/Yl2QCiWdwD+lygV5bMCvauzgu5PxBX/Yerg==", + "requires": { + "queue": "6.0.2" + } + } + } + }, + "@docusaurus/module-type-aliases": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-2.4.1.tgz", + "integrity": "sha512-gLBuIFM8Dp2XOCWffUDSjtxY7jQgKvYujt7Mx5s4FCTfoL5dN1EVbnrn+O2Wvh8b0a77D57qoIDY7ghgmatR1A==", + "requires": { + "@docusaurus/react-loadable": "5.5.2", + "@docusaurus/types": "2.4.1", + "@types/history": "^4.7.11", + "@types/react": "*", + "@types/react-router-config": "*", + "@types/react-router-dom": "*", + "react-helmet-async": "*", + "react-loadable": "npm:@docusaurus/react-loadable@5.5.2" + } + }, + "@docusaurus/plugin-content-blog": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-2.4.1.tgz", + "integrity": "sha512-E2i7Knz5YIbE1XELI6RlTnZnGgS52cUO4BlCiCUCvQHbR+s1xeIWz4C6BtaVnlug0Ccz7nFSksfwDpVlkujg5Q==", + "requires": { + "@docusaurus/core": "2.4.1", + "@docusaurus/logger": "2.4.1", + "@docusaurus/mdx-loader": "2.4.1", + "@docusaurus/types": "2.4.1", + "@docusaurus/utils": "2.4.1", + "@docusaurus/utils-common": "2.4.1", + "@docusaurus/utils-validation": "2.4.1", + "cheerio": "^1.0.0-rc.12", + "feed": "^4.2.2", + "fs-extra": "^10.1.0", + "lodash": "^4.17.21", + "reading-time": "^1.5.0", + "tslib": "^2.4.0", + "unist-util-visit": "^2.0.3", + "utility-types": "^3.10.0", + "webpack": "^5.73.0" + } + }, + "@docusaurus/plugin-content-docs": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-2.4.1.tgz", + "integrity": "sha512-Lo7lSIcpswa2Kv4HEeUcGYqaasMUQNpjTXpV0N8G6jXgZaQurqp7E8NGYeGbDXnb48czmHWbzDL4S3+BbK0VzA==", + "requires": { + "@docusaurus/core": "2.4.1", + "@docusaurus/logger": "2.4.1", + "@docusaurus/mdx-loader": "2.4.1", + "@docusaurus/module-type-aliases": "2.4.1", + "@docusaurus/types": "2.4.1", + "@docusaurus/utils": "2.4.1", + "@docusaurus/utils-validation": "2.4.1", + "@types/react-router-config": "^5.0.6", + "combine-promises": "^1.1.0", + "fs-extra": "^10.1.0", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "lodash": "^4.17.21", + "tslib": "^2.4.0", + "utility-types": "^3.10.0", + "webpack": "^5.73.0" + }, + "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "requires": { + "argparse": "^2.0.1" + } + } + } + }, + "@docusaurus/plugin-content-pages": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-2.4.1.tgz", + "integrity": "sha512-/UjuH/76KLaUlL+o1OvyORynv6FURzjurSjvn2lbWTFc4tpYY2qLYTlKpTCBVPhlLUQsfyFnshEJDLmPneq2oA==", + "requires": { + "@docusaurus/core": "2.4.1", + "@docusaurus/mdx-loader": "2.4.1", + "@docusaurus/types": "2.4.1", + "@docusaurus/utils": "2.4.1", + "@docusaurus/utils-validation": "2.4.1", + "fs-extra": "^10.1.0", + "tslib": "^2.4.0", + "webpack": "^5.73.0" + } + }, + "@docusaurus/plugin-debug": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-2.4.1.tgz", + "integrity": "sha512-7Yu9UPzRShlrH/G8btOpR0e6INFZr0EegWplMjOqelIwAcx3PKyR8mgPTxGTxcqiYj6hxSCRN0D8R7YrzImwNA==", + "requires": { + "@docusaurus/core": "2.4.1", + "@docusaurus/types": "2.4.1", + "@docusaurus/utils": "2.4.1", + "fs-extra": "^10.1.0", + "react-json-view": "^1.21.3", + "tslib": "^2.4.0" + } + }, + "@docusaurus/plugin-google-analytics": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-2.4.1.tgz", + "integrity": "sha512-dyZJdJiCoL+rcfnm0RPkLt/o732HvLiEwmtoNzOoz9MSZz117UH2J6U2vUDtzUzwtFLIf32KkeyzisbwUCgcaQ==", + "requires": { + "@docusaurus/core": "2.4.1", + "@docusaurus/types": "2.4.1", + "@docusaurus/utils-validation": "2.4.1", + "tslib": "^2.4.0" + } + }, + "@docusaurus/plugin-google-gtag": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-2.4.1.tgz", + "integrity": "sha512-mKIefK+2kGTQBYvloNEKtDmnRD7bxHLsBcxgnbt4oZwzi2nxCGjPX6+9SQO2KCN5HZbNrYmGo5GJfMgoRvy6uA==", + "requires": { + "@docusaurus/core": "2.4.1", + "@docusaurus/types": "2.4.1", + "@docusaurus/utils-validation": "2.4.1", + "tslib": "^2.4.0" + } + }, + "@docusaurus/plugin-google-tag-manager": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-2.4.1.tgz", + "integrity": "sha512-Zg4Ii9CMOLfpeV2nG74lVTWNtisFaH9QNtEw48R5QE1KIwDBdTVaiSA18G1EujZjrzJJzXN79VhINSbOJO/r3g==", + "requires": { + "@docusaurus/core": "2.4.1", + "@docusaurus/types": "2.4.1", + "@docusaurus/utils-validation": "2.4.1", + "tslib": "^2.4.0" + } + }, + "@docusaurus/plugin-sitemap": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-2.4.1.tgz", + "integrity": "sha512-lZx+ijt/+atQ3FVE8FOHV/+X3kuok688OydDXrqKRJyXBJZKgGjA2Qa8RjQ4f27V2woaXhtnyrdPop/+OjVMRg==", + "requires": { + "@docusaurus/core": "2.4.1", + "@docusaurus/logger": "2.4.1", + "@docusaurus/types": "2.4.1", + "@docusaurus/utils": "2.4.1", + "@docusaurus/utils-common": "2.4.1", + "@docusaurus/utils-validation": "2.4.1", + "fs-extra": "^10.1.0", + "sitemap": "^7.1.1", + "tslib": "^2.4.0" + } + }, + "@docusaurus/preset-classic": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-2.4.1.tgz", + "integrity": "sha512-P4//+I4zDqQJ+UDgoFrjIFaQ1MeS9UD1cvxVQaI6O7iBmiHQm0MGROP1TbE7HlxlDPXFJjZUK3x3cAoK63smGQ==", + "requires": { + "@docusaurus/core": "2.4.1", + "@docusaurus/plugin-content-blog": "2.4.1", + "@docusaurus/plugin-content-docs": "2.4.1", + "@docusaurus/plugin-content-pages": "2.4.1", + "@docusaurus/plugin-debug": "2.4.1", + "@docusaurus/plugin-google-analytics": "2.4.1", + "@docusaurus/plugin-google-gtag": "2.4.1", + "@docusaurus/plugin-google-tag-manager": "2.4.1", + "@docusaurus/plugin-sitemap": "2.4.1", + "@docusaurus/theme-classic": "2.4.1", + "@docusaurus/theme-common": "2.4.1", + "@docusaurus/theme-search-algolia": "2.4.1", + "@docusaurus/types": "2.4.1" + } + }, + "@docusaurus/react-loadable": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz", + "integrity": "sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ==", + "requires": { + "@types/react": "*", + "prop-types": "^15.6.2" + } + }, + "@docusaurus/theme-classic": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-2.4.1.tgz", + "integrity": "sha512-Rz0wKUa+LTW1PLXmwnf8mn85EBzaGSt6qamqtmnh9Hflkc+EqiYMhtUJeLdV+wsgYq4aG0ANc+bpUDpsUhdnwg==", + "requires": { + "@docusaurus/core": "2.4.1", + "@docusaurus/mdx-loader": "2.4.1", + "@docusaurus/module-type-aliases": "2.4.1", + "@docusaurus/plugin-content-blog": "2.4.1", + "@docusaurus/plugin-content-docs": "2.4.1", + "@docusaurus/plugin-content-pages": "2.4.1", + "@docusaurus/theme-common": "2.4.1", + "@docusaurus/theme-translations": "2.4.1", + "@docusaurus/types": "2.4.1", + "@docusaurus/utils": "2.4.1", + "@docusaurus/utils-common": "2.4.1", + "@docusaurus/utils-validation": "2.4.1", + "@mdx-js/react": "^1.6.22", + "clsx": "^1.2.1", + "copy-text-to-clipboard": "^3.0.1", + "infima": "0.2.0-alpha.43", + "lodash": "^4.17.21", + "nprogress": "^0.2.0", + "postcss": "^8.4.14", + "prism-react-renderer": "^1.3.5", + "prismjs": "^1.28.0", + "react-router-dom": "^5.3.3", + "rtlcss": "^3.5.0", + "tslib": "^2.4.0", + "utility-types": "^3.10.0" + } + }, + "@docusaurus/theme-common": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-2.4.1.tgz", + "integrity": "sha512-G7Zau1W5rQTaFFB3x3soQoZpkgMbl/SYNG8PfMFIjKa3M3q8n0m/GRf5/H/e5BqOvt8c+ZWIXGCiz+kUCSHovA==", + "requires": { + "@docusaurus/mdx-loader": "2.4.1", + "@docusaurus/module-type-aliases": "2.4.1", + "@docusaurus/plugin-content-blog": "2.4.1", + "@docusaurus/plugin-content-docs": "2.4.1", + "@docusaurus/plugin-content-pages": "2.4.1", + "@docusaurus/utils": "2.4.1", + "@docusaurus/utils-common": "2.4.1", + "@types/history": "^4.7.11", + "@types/react": "*", + "@types/react-router-config": "*", + "clsx": "^1.2.1", + "parse-numeric-range": "^1.3.0", + "prism-react-renderer": "^1.3.5", + "tslib": "^2.4.0", + "use-sync-external-store": "^1.2.0", + "utility-types": "^3.10.0" + } + }, + "@docusaurus/theme-mermaid": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-mermaid/-/theme-mermaid-2.4.1.tgz", + "integrity": "sha512-cM0ImKIqZfjmlaC+uAjep39kNBvb1bjz429QBHGs32maob4+UnRzVPPpCUCltyPVb4xjG5h1Tyq4pHzhtIikqA==", + "requires": { + "@docusaurus/core": "2.4.1", + "@docusaurus/module-type-aliases": "2.4.1", + "@docusaurus/theme-common": "2.4.1", + "@docusaurus/types": "2.4.1", + "@docusaurus/utils-validation": "2.4.1", + "@mdx-js/react": "^1.6.22", + "mermaid": "^9.2.2", + "tslib": "^2.4.0" + } + }, + "@docusaurus/theme-search-algolia": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-2.4.1.tgz", + "integrity": "sha512-6BcqW2lnLhZCXuMAvPRezFs1DpmEKzXFKlYjruuas+Xy3AQeFzDJKTJFIm49N77WFCTyxff8d3E4Q9pi/+5McQ==", + "requires": { + "@docsearch/react": "^3.1.1", + "@docusaurus/core": "2.4.1", + "@docusaurus/logger": "2.4.1", + "@docusaurus/plugin-content-docs": "2.4.1", + "@docusaurus/theme-common": "2.4.1", + "@docusaurus/theme-translations": "2.4.1", + "@docusaurus/utils": "2.4.1", + "@docusaurus/utils-validation": "2.4.1", + "algoliasearch": "^4.13.1", + "algoliasearch-helper": "^3.10.0", + "clsx": "^1.2.1", + "eta": "^2.0.0", + "fs-extra": "^10.1.0", + "lodash": "^4.17.21", + "tslib": "^2.4.0", + "utility-types": "^3.10.0" + } + }, + "@docusaurus/theme-translations": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-2.4.1.tgz", + "integrity": "sha512-T1RAGP+f86CA1kfE8ejZ3T3pUU3XcyvrGMfC/zxCtc2BsnoexuNI9Vk2CmuKCb+Tacvhxjv5unhxXce0+NKyvA==", + "requires": { + "fs-extra": "^10.1.0", + "tslib": "^2.4.0" + } + }, + "@docusaurus/types": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-2.4.1.tgz", + "integrity": "sha512-0R+cbhpMkhbRXX138UOc/2XZFF8hiZa6ooZAEEJFp5scytzCw4tC1gChMFXrpa3d2tYE6AX8IrOEpSonLmfQuQ==", + "requires": { + "@types/history": "^4.7.11", + "@types/react": "*", + "commander": "^5.1.0", + "joi": "^17.6.0", + "react-helmet-async": "^1.3.0", + "utility-types": "^3.10.0", + "webpack": "^5.73.0", + "webpack-merge": "^5.8.0" + }, + "dependencies": { + "commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==" + } + } + }, + "@docusaurus/utils": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-2.4.1.tgz", + "integrity": "sha512-1lvEZdAQhKNht9aPXPoh69eeKnV0/62ROhQeFKKxmzd0zkcuE/Oc5Gpnt00y/f5bIsmOsYMY7Pqfm/5rteT5GA==", + "requires": { + "@docusaurus/logger": "2.4.1", + "@svgr/webpack": "^6.2.1", + "escape-string-regexp": "^4.0.0", + "file-loader": "^6.2.0", + "fs-extra": "^10.1.0", + "github-slugger": "^1.4.0", + "globby": "^11.1.0", + "gray-matter": "^4.0.3", + "js-yaml": "^4.1.0", + "lodash": "^4.17.21", + "micromatch": "^4.0.5", + "resolve-pathname": "^3.0.0", + "shelljs": "^0.8.5", + "tslib": "^2.4.0", + "url-loader": "^4.1.1", + "webpack": "^5.73.0" + }, + "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "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==" + }, + "fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "requires": { + "argparse": "^2.0.1" + } + } + } + }, + "@docusaurus/utils-common": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-2.4.1.tgz", + "integrity": "sha512-bCVGdZU+z/qVcIiEQdyx0K13OC5mYwxhSuDUR95oFbKVuXYRrTVrwZIqQljuo1fyJvFTKHiL9L9skQOPokuFNQ==", + "requires": { + "tslib": "^2.4.0" + } + }, + "@docusaurus/utils-validation": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-2.4.1.tgz", + "integrity": "sha512-unII3hlJlDwZ3w8U+pMO3Lx3RhI4YEbY3YNsQj4yzrkZzlpqZOLuAiZK2JyULnD+TKbceKU0WyWkQXtYbLNDFA==", + "requires": { + "@docusaurus/logger": "2.4.1", + "@docusaurus/utils": "2.4.1", + "joi": "^17.6.0", + "js-yaml": "^4.1.0", + "tslib": "^2.4.0" + }, + "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "requires": { + "argparse": "^2.0.1" + } + } + } + }, + "@esbuild/aix-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", + "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", + "dev": true, + "optional": true + }, + "@esbuild/android-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", + "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", + "dev": true, + "optional": true + }, + "@esbuild/android-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", + "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", + "dev": true, + "optional": true + }, + "@esbuild/android-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", + "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", + "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", + "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", + "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", + "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", + "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", + "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", + "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", + "dev": true, + "optional": true + }, + "@esbuild/linux-loong64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", + "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", + "dev": true, + "optional": true + }, + "@esbuild/linux-mips64el": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", + "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", + "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", + "dev": true, + "optional": true + }, + "@esbuild/linux-riscv64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", + "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", + "dev": true, + "optional": true + }, + "@esbuild/linux-s390x": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", + "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", + "dev": true, + "optional": true + }, + "@esbuild/linux-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", + "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", + "dev": true, + "optional": true + }, + "@esbuild/netbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", + "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", + "dev": true, + "optional": true + }, + "@esbuild/openbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", + "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", + "dev": true, + "optional": true + }, + "@esbuild/sunos-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", + "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", + "dev": true, + "optional": true + }, + "@esbuild/win32-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", + "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", + "dev": true, + "optional": true + }, + "@esbuild/win32-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", + "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", + "dev": true, + "optional": true + }, + "@esbuild/win32-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", + "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", + "dev": true, + "optional": true + }, + "@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^3.3.0" + } + }, + "@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true + }, + "@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "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 + }, + "globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + } + } + }, + "@eslint/js": { + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.48.0.tgz", + "integrity": "sha512-ZSjtmelB7IJfWD2Fvb7+Z+ChTIKWq6kjda95fLcQKNS5aheVHn4IkfgRQE3sIIzTcSLwLcLZUD9UBt+V7+h+Pw==", + "dev": true + }, + "@fast-csv/format": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/@fast-csv/format/-/format-4.3.5.tgz", + "integrity": "sha512-8iRn6QF3I8Ak78lNAa+Gdl5MJJBM5vRHivFtMRUWINdevNo00K7OXxS2PshawLKTejVwieIlPmK5YlLu6w4u8A==", + "requires": { + "@types/node": "^14.0.1", + "lodash.escaperegexp": "^4.1.2", + "lodash.isboolean": "^3.0.3", + "lodash.isequal": "^4.5.0", + "lodash.isfunction": "^3.0.9", + "lodash.isnil": "^4.0.0" + }, + "dependencies": { + "@types/node": { + "version": "14.18.33", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.33.tgz", + "integrity": "sha512-qelS/Ra6sacc4loe/3MSjXNL1dNQ/GjxNHVzuChwMfmk7HuycRLVQN2qNY3XahK+fZc5E2szqQSKUyAF0E+2bg==" + } + } + }, + "@fast-csv/parse": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/@fast-csv/parse/-/parse-4.3.6.tgz", + "integrity": "sha512-uRsLYksqpbDmWaSmzvJcuApSEe38+6NQZBUsuAyMZKqHxH0g1wcJgsKUvN3WC8tewaqFjBMMGrkHmC+T7k8LvA==", + "requires": { + "@types/node": "^14.0.1", + "lodash.escaperegexp": "^4.1.2", + "lodash.groupby": "^4.6.0", + "lodash.isfunction": "^3.0.9", + "lodash.isnil": "^4.0.0", + "lodash.isundefined": "^3.0.1", + "lodash.uniq": "^4.5.0" + }, + "dependencies": { + "@types/node": { + "version": "14.18.33", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.33.tgz", + "integrity": "sha512-qelS/Ra6sacc4loe/3MSjXNL1dNQ/GjxNHVzuChwMfmk7HuycRLVQN2qNY3XahK+fZc5E2szqQSKUyAF0E+2bg==" + } + } + }, + "@gar/promisify": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", + "optional": true + }, + "@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" + }, + "@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "requires": { + "@hapi/hoek": "^9.0.0" + } + }, + "@humanwhocodes/config-array": { + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + } + }, + "@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true + }, + "@humanwhocodes/object-schema": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", + "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", + "dev": true + }, + "@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + } + }, + "@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true + }, + "@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + } + }, + "@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, + "requires": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + } + }, + "@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "requires": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + } + }, + "@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "requires": { + "jest-get-type": "^29.6.3" + } + }, + "@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + } + }, + "@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, + "requires": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + } + }, + "@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, + "requires": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + } + }, + "@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "requires": { + "@sinclair/typebox": "^0.27.8" + } + }, + "@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + } + }, + "@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "requires": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + } + }, + "@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "dev": true, + "requires": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + } + }, + "@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "requires": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "dependencies": { + "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 + } + } + }, + "@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "requires": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + } + }, + "@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "requires": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==" + }, + "@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==" + }, + "@jridgewell/source-map": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", + "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", + "requires": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + }, + "@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "requires": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "@jsdoc/salty": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.5.tgz", + "integrity": "sha512-TfRP53RqunNe2HBobVBJ0VLhK1HbfvBYeTC1ahnN64PWvyYyGebmMiPkuwvD9fpw2ZbkoPb8Q7mwy0aR8Z9rvw==", + "requires": { + "lodash": "^4.17.21" + } + }, + "@jvalue/eslint-config-jvalue": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@jvalue/eslint-config-jvalue/-/eslint-config-jvalue-1.3.0.tgz", + "integrity": "sha512-IxPAMboZ8qcpKD07LE6/Q0tKpQUm+1026Y4684G6FfTJ0hNPTLtcnOyLDs47LgNToG93O0pIdnIsbTtgoPdb1Q==", + "dev": true + }, + "@leichtgewicht/ip-codec": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", + "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" + }, + "@mapbox/node-pre-gyp": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz", + "integrity": "sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA==", + "requires": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "dependencies": { + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@mdx-js/mdx": { + "version": "1.6.22", + "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-1.6.22.tgz", + "integrity": "sha512-AMxuLxPz2j5/6TpF/XSdKpQP1NlG0z11dFOlq+2IP/lSgl11GY8ji6S/rgsViN/L0BDvHvUMruRb7ub+24LUYA==", + "requires": { + "@babel/core": "7.12.9", + "@babel/plugin-syntax-jsx": "7.12.1", + "@babel/plugin-syntax-object-rest-spread": "7.8.3", + "@mdx-js/util": "1.6.22", + "babel-plugin-apply-mdx-type-prop": "1.6.22", + "babel-plugin-extract-import-names": "1.6.22", + "camelcase-css": "2.0.1", + "detab": "2.0.4", + "hast-util-raw": "6.0.1", + "lodash.uniq": "4.5.0", + "mdast-util-to-hast": "10.0.1", + "remark-footnotes": "2.0.0", + "remark-mdx": "1.6.22", + "remark-parse": "8.0.3", + "remark-squeeze-paragraphs": "4.0.0", + "style-to-object": "0.3.0", + "unified": "9.2.0", + "unist-builder": "2.0.3", + "unist-util-visit": "2.0.3" + }, + "dependencies": { + "@babel/core": { + "version": "7.12.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.9.tgz", + "integrity": "sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ==", + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.12.5", + "@babel/helper-module-transforms": "^7.12.1", + "@babel/helpers": "^7.12.5", + "@babel/parser": "^7.12.7", + "@babel/template": "^7.12.7", + "@babel/traverse": "^7.12.9", + "@babel/types": "^7.12.7", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.2", + "lodash": "^4.17.19", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + } + }, + "@babel/plugin-syntax-jsx": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.1.tgz", + "integrity": "sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==" + }, + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==" + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==" + }, + "unified": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.0.tgz", + "integrity": "sha512-vx2Z0vY+a3YoTj8+pttM3tiJHCwY5UFbYdiWrwBEbHmK8pvsPj2rtAX2BFfgXen8T39CJWblWRDT4L5WGXtDdg==", + "requires": { + "bail": "^1.0.0", + "extend": "^3.0.0", + "is-buffer": "^2.0.0", + "is-plain-obj": "^2.0.0", + "trough": "^1.0.0", + "vfile": "^4.0.0" + } + } + } + }, + "@mdx-js/react": { + "version": "1.6.22", + "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-1.6.22.tgz", + "integrity": "sha512-TDoPum4SHdfPiGSAaRBw7ECyI8VaHpK8GJugbJIJuqyh6kzw9ZLJZW3HGL3NNrJGxcAixUvqROm+YuQOo5eXtg==" + }, + "@mdx-js/util": { + "version": "1.6.22", + "resolved": "https://registry.npmjs.org/@mdx-js/util/-/util-1.6.22.tgz", + "integrity": "sha512-H1rQc1ZOHANWBvPcW+JpGwr+juXSxM8Q8YCkm3GhZd8REu1fHR3z99CErO1p9pkcfcxZnMdIZdIsXkOHY0NilA==" + }, + "@nicolo-ribaudo/eslint-scope-5-internals": { + "version": "5.1.1-v1", + "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", + "integrity": "sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==", + "dev": true, + "requires": { + "eslint-scope": "5.1.1" + } + }, + "@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==", + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" + }, + "@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==", + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@npmcli/fs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", + "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", + "optional": true, + "requires": { + "@gar/promisify": "^1.0.1", + "semver": "^7.3.5" + }, + "dependencies": { + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "optional": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@npmcli/move-file": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", + "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "optional": true, + "requires": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + } + }, + "@nrwl/devkit": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/@nrwl/devkit/-/devkit-15.3.0.tgz", + "integrity": "sha512-1O9QLB/eYS6ddw4MZnV4yj4CEqLIbpleZZiG/9w1TaiVO/jfNfXVaxc8EA87XSzMpk2W+/4Qggmabt6gAQaabA==", + "dev": true, + "requires": { + "@phenomnomnominal/tsquery": "4.1.1", + "ejs": "^3.1.7", + "ignore": "^5.0.4", + "semver": "7.3.4", + "tslib": "^2.3.0" + } + }, + "@nrwl/esbuild": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nrwl/esbuild/-/esbuild-18.0.1.tgz", + "integrity": "sha512-qUcPnv1q9arcBKSOu0OoBVShmQdEP2p3ejnqFbeiuRxdVhAWzO1j1oGajuer7W37Ii+baoS8OZONdkeYYYCNhg==", + "dev": true, + "requires": { + "@nx/esbuild": "18.0.1" + } + }, + "@nrwl/eslint-plugin-nx": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nrwl/eslint-plugin-nx/-/eslint-plugin-nx-18.0.1.tgz", + "integrity": "sha512-/E7b09yLtFHf9QCfoqqZpw+HGBnH/Bz1pbZTAvY8zdduhDYO6Em5HRy+Y40pfuWJPUcOgA50j2zNGR+sTCrtRA==", + "dev": true, + "requires": { + "@nx/eslint-plugin": "18.0.1" + } + }, + "@nrwl/jest": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nrwl/jest/-/jest-18.0.1.tgz", + "integrity": "sha512-j/gd653kPU5qJuYU5RoT2kmy6wmP+GTIL6CtkqlOJ7G38Xbk38JWT+i0fdoAkG5kdsWJIxgDASWxgGocKFOR8w==", + "dev": true, + "requires": { + "@nx/jest": "18.0.1" + } + }, + "@nrwl/js": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nrwl/js/-/js-18.3.4.tgz", + "integrity": "sha512-oyiMoxzDVGQe5E4UFGO/WAOU211HEIdRxSEOfs1lPhvA8lKbUa0IWDqPOugNws/YHAr+vUTU3sZDJ3uU3RJuYQ==", + "dev": true, + "requires": { + "@nx/js": "18.3.4" + } + }, + "@nrwl/node": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nrwl/node/-/node-18.0.1.tgz", + "integrity": "sha512-H9wVxteX7vrwIRIOgZgST/eBCBCJIHzVnwmGdI2kGn451zSWpcVDSocD1ANlOhziPqqf1ym4mAtacosjKC1uFw==", + "dev": true, + "requires": { + "@nx/node": "18.0.1" + } + }, + "@nrwl/react": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nrwl/react/-/react-18.0.1.tgz", + "integrity": "sha512-/YiOdu/GeOd9cXwaY3iZ+sz0m4Dm8L7ZG7YxMK5mcOhIRjyY6JBsig3zBXkAY/JC1IGfXj6pMW54L2F2J43b9g==", + "dev": true, + "requires": { + "@nx/react": "18.0.1" + } + }, + "@nrwl/rollup": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nrwl/rollup/-/rollup-18.0.1.tgz", + "integrity": "sha512-ObCgvfgbmu13f3EFGqi59mGzHa4WGB9pYQPaYcXy2xLF/kiOhsa5D1fqhYjgbqyHPGOFWdJRsac9qsf1Re/R+w==", + "dev": true, + "requires": { + "@nx/rollup": "18.0.1" + } + }, + "@nrwl/tao": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nrwl/tao/-/tao-18.0.1.tgz", + "integrity": "sha512-marSAWRyBAiXciwE+893ptwB6kHR+BKxqERBvH6/+2TWhbnOdC8Czf2VnmQIgIjL+bg+76UUZPt5B2r+qGfeAg==", + "dev": true, + "requires": { + "nx": "18.0.1", + "tslib": "^2.3.0" + } + }, + "@nrwl/vite": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nrwl/vite/-/vite-18.3.4.tgz", + "integrity": "sha512-0lUQqgui2oWFLUK9bT5XwyyC321tmiKetRxuSvzv+hJZQbMBz6LrYLAWXyEHtLkSVszBqeh8rt8hcrXcIxDfCA==", + "dev": true, + "requires": { + "@nx/vite": "18.3.4" + } + }, + "@nrwl/web": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nrwl/web/-/web-18.3.4.tgz", + "integrity": "sha512-Zdi0WCAq6+JD1/H8FwzGNjSrHNZWMTN8uPCCV4re3rr2M7oJ+0NqMTATHNqsWGMr2C3HANRuxqC9wDa9Av0XCQ==", + "dev": true, + "requires": { + "@nx/web": "18.3.4" + } + }, + "@nrwl/webpack": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nrwl/webpack/-/webpack-18.0.1.tgz", + "integrity": "sha512-lVWO7Y8yZi9k1qzJ/Z2bCGPdELlZ9RmlOPzV8yV1Hl9G/cQE8F4W7dUyGe0R9oQNVSwMKbNbHQLvlKOhU+GsBA==", + "dev": true, + "requires": { + "@nx/webpack": "18.0.1" + } + }, + "@nrwl/workspace": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nrwl/workspace/-/workspace-18.0.1.tgz", + "integrity": "sha512-kkPaSYUAprEWNgXejBgY7DsSn83A6e0wh7jBOY+ou63Al9THWkqYVaAQ/5F8i65HxUlIuCSxhYvKMUoaI/yOLQ==", + "dev": true, + "requires": { + "@nx/workspace": "18.0.1" + } + }, + "@nx-plus/docusaurus": { + "version": "15.0.0-rc.0", + "resolved": "https://registry.npmjs.org/@nx-plus/docusaurus/-/docusaurus-15.0.0-rc.0.tgz", + "integrity": "sha512-GmoYHKljQJvJLSYhWBe+huStwJbm+RbK+lC2j2sA+ZCUdkUo+P2Yv5oQGbqWU4jXclYe8pOP/qVFJs3j3Zjw/Q==", + "dev": true, + "requires": { + "@nrwl/devkit": "^15.0.0" + } + }, + "@nx/devkit": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/devkit/-/devkit-18.0.1.tgz", + "integrity": "sha512-YmX5YqZAGqIK6ACwj6+BmogbOr/HVrILnx5ybiHPwNzMTv5HQpJ67HfBRYUDKIFiY4zTjciYyWAmqG89UYCq1w==", + "dev": true, + "requires": { + "@nrwl/devkit": "18.0.1", + "ejs": "^3.1.7", + "enquirer": "~2.3.6", + "ignore": "^5.0.4", + "semver": "^7.5.3", + "tmp": "~0.2.1", + "tslib": "^2.3.0", + "yargs-parser": "21.1.1" + }, + "dependencies": { + "@nrwl/devkit": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nrwl/devkit/-/devkit-18.0.1.tgz", + "integrity": "sha512-9SXlKk+LSNMVaKE6RqjIZ2agPKQVljOrt2cMyQe0SQCsqxrzqajwGD19zDGISiOLiiq24QPz30Q+vM7W98Yb9g==", + "dev": true, + "requires": { + "@nx/devkit": "18.0.1" + } + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@nx/esbuild": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/esbuild/-/esbuild-18.0.1.tgz", + "integrity": "sha512-a/SC22lRW6VKPngkYemQ2ZP5eiG0OBCWSYikyZi047vKQX2OM01uqRHzsspzCU0gJ7rl0Sb/xjH1L+PSPBEXRg==", + "dev": true, + "requires": { + "@nrwl/esbuild": "18.0.1", + "@nx/devkit": "18.0.1", + "@nx/js": "18.0.1", + "chalk": "^4.1.0", + "fast-glob": "3.2.7", + "fs-extra": "^11.1.0", + "tsconfig-paths": "^4.1.2", + "tslib": "^2.3.0" + }, + "dependencies": { + "@nrwl/js": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nrwl/js/-/js-18.0.1.tgz", + "integrity": "sha512-O1wfFdDMvObgqZgH/5qebm9YeN5cJ+UL2fJV2ZnsWVz3e8jhOQLflN3J7jXUWxzsz5stYiplsGpBjgXQRcNwaQ==", + "dev": true, + "requires": { + "@nx/js": "18.0.1" + } + }, + "@nx/js": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/js/-/js-18.0.1.tgz", + "integrity": "sha512-U/l8K09UMrE4zVXEpmwpRWaY4fCrdcaaB3NXsEUwghJE6OuacADdaQ5ZWPxa0ji3nARKW7Umj87sOlUu8YjpTQ==", + "dev": true, + "requires": { + "@babel/core": "^7.23.2", + "@babel/plugin-proposal-decorators": "^7.22.7", + "@babel/plugin-transform-class-properties": "^7.22.5", + "@babel/plugin-transform-runtime": "^7.23.2", + "@babel/preset-env": "^7.23.2", + "@babel/preset-typescript": "^7.22.5", + "@babel/runtime": "^7.22.6", + "@nrwl/js": "18.0.1", + "@nx/devkit": "18.0.1", + "@nx/workspace": "18.0.1", + "@phenomnomnominal/tsquery": "~5.0.1", + "babel-plugin-const-enum": "^1.0.1", + "babel-plugin-macros": "^2.8.0", + "babel-plugin-transform-typescript-metadata": "^0.3.1", + "chalk": "^4.1.0", + "columnify": "^1.6.0", + "detect-port": "^1.5.1", + "fast-glob": "3.2.7", + "fs-extra": "^11.1.0", + "ignore": "^5.0.4", + "js-tokens": "^4.0.0", + "minimatch": "9.0.3", + "npm-package-arg": "11.0.1", + "npm-run-path": "^4.0.1", + "ora": "5.3.0", + "semver": "^7.5.3", + "source-map-support": "0.5.19", + "ts-node": "10.9.1", + "tsconfig-paths": "^4.1.2", + "tslib": "^2.3.0" + } + }, + "@phenomnomnominal/tsquery": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@phenomnomnominal/tsquery/-/tsquery-5.0.1.tgz", + "integrity": "sha512-3nVv+e2FQwsW8Aw6qTU6f+1rfcJ3hrcnvH/mu9i8YhxO+9sqbOfpL8m6PbET5+xKOlz/VSbp0RoYWYCtIsnmuA==", + "dev": true, + "requires": { + "esquery": "^1.4.0" + } + }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true + }, + "tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "requires": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + } + } + }, + "@nx/eslint": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/eslint/-/eslint-18.0.1.tgz", + "integrity": "sha512-0IZOFt/58f47TC2atMa1ClmA3lKrflnG8YEJznvy86iwHMARxCSxsBEK+DKxMSeFMKBbxuC26aN+9MweFGohKA==", + "dev": true, + "requires": { + "@nx/devkit": "18.0.1", + "@nx/js": "18.0.1", + "@nx/linter": "18.0.1", + "eslint": "^8.0.0", + "tslib": "^2.3.0", + "typescript": "~5.3.2" + }, + "dependencies": { + "@nrwl/js": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nrwl/js/-/js-18.0.1.tgz", + "integrity": "sha512-O1wfFdDMvObgqZgH/5qebm9YeN5cJ+UL2fJV2ZnsWVz3e8jhOQLflN3J7jXUWxzsz5stYiplsGpBjgXQRcNwaQ==", + "dev": true, + "requires": { + "@nx/js": "18.0.1" + } + }, + "@nx/js": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/js/-/js-18.0.1.tgz", + "integrity": "sha512-U/l8K09UMrE4zVXEpmwpRWaY4fCrdcaaB3NXsEUwghJE6OuacADdaQ5ZWPxa0ji3nARKW7Umj87sOlUu8YjpTQ==", + "dev": true, + "requires": { + "@babel/core": "^7.23.2", + "@babel/plugin-proposal-decorators": "^7.22.7", + "@babel/plugin-transform-class-properties": "^7.22.5", + "@babel/plugin-transform-runtime": "^7.23.2", + "@babel/preset-env": "^7.23.2", + "@babel/preset-typescript": "^7.22.5", + "@babel/runtime": "^7.22.6", + "@nrwl/js": "18.0.1", + "@nx/devkit": "18.0.1", + "@nx/workspace": "18.0.1", + "@phenomnomnominal/tsquery": "~5.0.1", + "babel-plugin-const-enum": "^1.0.1", + "babel-plugin-macros": "^2.8.0", + "babel-plugin-transform-typescript-metadata": "^0.3.1", + "chalk": "^4.1.0", + "columnify": "^1.6.0", + "detect-port": "^1.5.1", + "fast-glob": "3.2.7", + "fs-extra": "^11.1.0", + "ignore": "^5.0.4", + "js-tokens": "^4.0.0", + "minimatch": "9.0.3", + "npm-package-arg": "11.0.1", + "npm-run-path": "^4.0.1", + "ora": "5.3.0", + "semver": "^7.5.3", + "source-map-support": "0.5.19", + "ts-node": "10.9.1", + "tsconfig-paths": "^4.1.2", + "tslib": "^2.3.0" + } + }, + "@phenomnomnominal/tsquery": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@phenomnomnominal/tsquery/-/tsquery-5.0.1.tgz", + "integrity": "sha512-3nVv+e2FQwsW8Aw6qTU6f+1rfcJ3hrcnvH/mu9i8YhxO+9sqbOfpL8m6PbET5+xKOlz/VSbp0RoYWYCtIsnmuA==", + "dev": true, + "requires": { + "esquery": "^1.4.0" + } + }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true + }, + "tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "requires": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + } + } + }, + "@nx/eslint-plugin": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/eslint-plugin/-/eslint-plugin-18.0.1.tgz", + "integrity": "sha512-RGFGKZWh3EsRq+vJjdsFk5zkBLSZJrYoMQpZufudHcT2+WY61UkbVOdWTwG0GdegcPCDU9+UztDAH3HXCOeQaw==", + "dev": true, + "requires": { + "@nrwl/eslint-plugin-nx": "18.0.1", + "@nx/devkit": "18.0.1", + "@nx/js": "18.0.1", + "@typescript-eslint/type-utils": "^6.13.2", + "@typescript-eslint/utils": "^6.13.2", + "chalk": "^4.1.0", + "confusing-browser-globals": "^1.0.9", + "jsonc-eslint-parser": "^2.1.0", + "semver": "^7.5.3", + "tslib": "^2.3.0" + }, + "dependencies": { + "@nrwl/js": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nrwl/js/-/js-18.0.1.tgz", + "integrity": "sha512-O1wfFdDMvObgqZgH/5qebm9YeN5cJ+UL2fJV2ZnsWVz3e8jhOQLflN3J7jXUWxzsz5stYiplsGpBjgXQRcNwaQ==", + "dev": true, + "requires": { + "@nx/js": "18.0.1" + } + }, + "@nx/js": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/js/-/js-18.0.1.tgz", + "integrity": "sha512-U/l8K09UMrE4zVXEpmwpRWaY4fCrdcaaB3NXsEUwghJE6OuacADdaQ5ZWPxa0ji3nARKW7Umj87sOlUu8YjpTQ==", + "dev": true, + "requires": { + "@babel/core": "^7.23.2", + "@babel/plugin-proposal-decorators": "^7.22.7", + "@babel/plugin-transform-class-properties": "^7.22.5", + "@babel/plugin-transform-runtime": "^7.23.2", + "@babel/preset-env": "^7.23.2", + "@babel/preset-typescript": "^7.22.5", + "@babel/runtime": "^7.22.6", + "@nrwl/js": "18.0.1", + "@nx/devkit": "18.0.1", + "@nx/workspace": "18.0.1", + "@phenomnomnominal/tsquery": "~5.0.1", + "babel-plugin-const-enum": "^1.0.1", + "babel-plugin-macros": "^2.8.0", + "babel-plugin-transform-typescript-metadata": "^0.3.1", + "chalk": "^4.1.0", + "columnify": "^1.6.0", + "detect-port": "^1.5.1", + "fast-glob": "3.2.7", + "fs-extra": "^11.1.0", + "ignore": "^5.0.4", + "js-tokens": "^4.0.0", + "minimatch": "9.0.3", + "npm-package-arg": "11.0.1", + "npm-run-path": "^4.0.1", + "ora": "5.3.0", + "semver": "^7.5.3", + "source-map-support": "0.5.19", + "ts-node": "10.9.1", + "tsconfig-paths": "^4.1.2", + "tslib": "^2.3.0" + }, + "dependencies": { + "fast-glob": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", + "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + } + } + }, + "@phenomnomnominal/tsquery": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@phenomnomnominal/tsquery/-/tsquery-5.0.1.tgz", + "integrity": "sha512-3nVv+e2FQwsW8Aw6qTU6f+1rfcJ3hrcnvH/mu9i8YhxO+9sqbOfpL8m6PbET5+xKOlz/VSbp0RoYWYCtIsnmuA==", + "dev": true, + "requires": { + "esquery": "^1.4.0" + } + }, + "@typescript-eslint/scope-manager": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.20.0.tgz", + "integrity": "sha512-p4rvHQRDTI1tGGMDFQm+GtxP1ZHyAh64WANVoyEcNMpaTFn3ox/3CcgtIlELnRfKzSs/DwYlDccJEtr3O6qBvA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.20.0", + "@typescript-eslint/visitor-keys": "6.20.0" + } + }, + "@typescript-eslint/types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.20.0.tgz", + "integrity": "sha512-MM9mfZMAhiN4cOEcUOEx+0HmuaW3WBfukBZPCfwSqFnQy0grXYtngKCqpQN339X3RrwtzspWJrpbrupKYUSBXQ==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.20.0.tgz", + "integrity": "sha512-RnRya9q5m6YYSpBN7IzKu9FmLcYtErkDkc8/dKv81I9QiLLtVBHrjz+Ev/crAqgMNW2FCsoZF4g2QUylMnJz+g==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.20.0", + "@typescript-eslint/visitor-keys": "6.20.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "dependencies": { + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@typescript-eslint/utils": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.20.0.tgz", + "integrity": "sha512-/EKuw+kRu2vAqCoDwDCBtDRU6CTKbUmwwI7SH7AashZ+W+7o8eiyy6V2cdOqN49KsTcASWsC5QeghYuRDTyOOg==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.20.0", + "@typescript-eslint/types": "6.20.0", + "@typescript-eslint/typescript-estree": "6.20.0", + "semver": "^7.5.4" + }, + "dependencies": { + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@typescript-eslint/visitor-keys": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.20.0.tgz", + "integrity": "sha512-E8Cp98kRe4gKHjJD4NExXKz/zOJ1A2hhZc+IMVD6i7w4yjIvh6VyuRI0gRtxAsXtoC35uGMaQ9rjI2zJaXDEAw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.20.0", + "eslint-visitor-keys": "^3.4.1" + } + }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, + "minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "semver": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", + "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true + }, + "tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "requires": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + } + } + }, + "@nx/jest": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/jest/-/jest-18.0.1.tgz", + "integrity": "sha512-ILBur8l4pKxpBCPQzIooPxGpV2NcwK2qLydskZhxyu+vm36kp3py5CyKo+j8HLlskJ7M6QGZf08l3DgsG+CYbQ==", + "dev": true, + "requires": { + "@jest/reporters": "^29.4.1", + "@jest/test-result": "^29.4.1", + "@nrwl/jest": "18.0.1", + "@nx/devkit": "18.0.1", + "@nx/js": "18.0.1", + "@phenomnomnominal/tsquery": "~5.0.1", + "chalk": "^4.1.0", + "identity-obj-proxy": "3.0.0", + "jest-config": "^29.4.1", + "jest-resolve": "^29.4.1", + "jest-util": "^29.4.1", + "minimatch": "9.0.3", + "resolve.exports": "1.1.0", + "tslib": "^2.3.0" + }, + "dependencies": { + "@nrwl/js": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nrwl/js/-/js-18.0.1.tgz", + "integrity": "sha512-O1wfFdDMvObgqZgH/5qebm9YeN5cJ+UL2fJV2ZnsWVz3e8jhOQLflN3J7jXUWxzsz5stYiplsGpBjgXQRcNwaQ==", + "dev": true, + "requires": { + "@nx/js": "18.0.1" + } + }, + "@nx/js": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/js/-/js-18.0.1.tgz", + "integrity": "sha512-U/l8K09UMrE4zVXEpmwpRWaY4fCrdcaaB3NXsEUwghJE6OuacADdaQ5ZWPxa0ji3nARKW7Umj87sOlUu8YjpTQ==", + "dev": true, + "requires": { + "@babel/core": "^7.23.2", + "@babel/plugin-proposal-decorators": "^7.22.7", + "@babel/plugin-transform-class-properties": "^7.22.5", + "@babel/plugin-transform-runtime": "^7.23.2", + "@babel/preset-env": "^7.23.2", + "@babel/preset-typescript": "^7.22.5", + "@babel/runtime": "^7.22.6", + "@nrwl/js": "18.0.1", + "@nx/devkit": "18.0.1", + "@nx/workspace": "18.0.1", + "@phenomnomnominal/tsquery": "~5.0.1", + "babel-plugin-const-enum": "^1.0.1", + "babel-plugin-macros": "^2.8.0", + "babel-plugin-transform-typescript-metadata": "^0.3.1", + "chalk": "^4.1.0", + "columnify": "^1.6.0", + "detect-port": "^1.5.1", + "fast-glob": "3.2.7", + "fs-extra": "^11.1.0", + "ignore": "^5.0.4", + "js-tokens": "^4.0.0", + "minimatch": "9.0.3", + "npm-package-arg": "11.0.1", + "npm-run-path": "^4.0.1", + "ora": "5.3.0", + "semver": "^7.5.3", + "source-map-support": "0.5.19", + "ts-node": "10.9.1", + "tsconfig-paths": "^4.1.2", + "tslib": "^2.3.0" + } + }, + "@phenomnomnominal/tsquery": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@phenomnomnominal/tsquery/-/tsquery-5.0.1.tgz", + "integrity": "sha512-3nVv+e2FQwsW8Aw6qTU6f+1rfcJ3hrcnvH/mu9i8YhxO+9sqbOfpL8m6PbET5+xKOlz/VSbp0RoYWYCtIsnmuA==", + "dev": true, + "requires": { + "esquery": "^1.4.0" + } + }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true + }, + "tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "requires": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + } + } + }, + "@nx/js": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nx/js/-/js-18.3.4.tgz", + "integrity": "sha512-+MPacp/B09e5QwaFQBkev9pW862ZpmesqR2lUUnFAXUBKtjYVIAmhJWHOtevqC1om4OxvTsbluYHsbAkAUzlMA==", + "dev": true, + "requires": { + "@babel/core": "^7.23.2", + "@babel/plugin-proposal-decorators": "^7.22.7", + "@babel/plugin-transform-class-properties": "^7.22.5", + "@babel/plugin-transform-runtime": "^7.23.2", + "@babel/preset-env": "^7.23.2", + "@babel/preset-typescript": "^7.22.5", + "@babel/runtime": "^7.22.6", + "@nrwl/js": "18.3.4", + "@nx/devkit": "18.3.4", + "@nx/workspace": "18.3.4", + "@phenomnomnominal/tsquery": "~5.0.1", + "babel-plugin-const-enum": "^1.0.1", + "babel-plugin-macros": "^2.8.0", + "babel-plugin-transform-typescript-metadata": "^0.3.1", + "chalk": "^4.1.0", + "columnify": "^1.6.0", + "detect-port": "^1.5.1", + "fast-glob": "3.2.7", + "fs-extra": "^11.1.0", + "ignore": "^5.0.4", + "js-tokens": "^4.0.0", + "minimatch": "9.0.3", + "npm-package-arg": "11.0.1", + "npm-run-path": "^4.0.1", + "ora": "5.3.0", + "semver": "^7.5.3", + "source-map-support": "0.5.19", + "ts-node": "10.9.1", + "tsconfig-paths": "^4.1.2", + "tslib": "^2.3.0" + }, + "dependencies": { + "@nrwl/devkit": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nrwl/devkit/-/devkit-18.3.4.tgz", + "integrity": "sha512-Fty9Huqm12OYueU3uLJl3uvBUl5BvEyPfvw8+rLiNx9iftdEattM8C+268eAbIRRSLSOVXlWsJH4brlc6QZYYw==", + "dev": true, + "requires": { + "@nx/devkit": "18.3.4" + } + }, + "@nrwl/tao": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nrwl/tao/-/tao-18.3.4.tgz", + "integrity": "sha512-+7KsDYmGj1cvNaXZcjSYOPN1h17hsGFBtVX7MqnpJLLkQTUhKg2rQxqyluzshJ+RoDUVtYPGyHg1AizlB66RIA==", + "dev": true, + "requires": { + "nx": "18.3.4", + "tslib": "^2.3.0" + } + }, + "@nrwl/workspace": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nrwl/workspace/-/workspace-18.3.4.tgz", + "integrity": "sha512-ziPHZcSYj46aPYrRHaKu56/SmYCijLT5vIm/UaoWD5v5Fy5CRigO/ezUImsHGHMEZWfHt44s4jsv7QdJWAXe7w==", + "dev": true, + "requires": { + "@nx/workspace": "18.3.4" + } + }, + "@nx/devkit": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nx/devkit/-/devkit-18.3.4.tgz", + "integrity": "sha512-M3htxl5WvlNKK5KNOndCAApbyBCZNTFFs+rtdwvudNZk5+84zAAPaWzSoX9C4XLAW78/f98LzF68/ch05aN12A==", + "dev": true, + "requires": { + "@nrwl/devkit": "18.3.4", + "ejs": "^3.1.7", + "enquirer": "~2.3.6", + "ignore": "^5.0.4", + "semver": "^7.5.3", + "tmp": "~0.2.1", + "tslib": "^2.3.0", + "yargs-parser": "21.1.1" + } + }, + "@nx/nx-darwin-arm64": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nx/nx-darwin-arm64/-/nx-darwin-arm64-18.3.4.tgz", + "integrity": "sha512-MOGk9z4fIoOkJB68diH3bwoWrC8X9IzMNsz1mu0cbVfgCRAfIV3b+lMsiwQYzWal3UWW5DE5Rkss4F8whiV5Uw==", + "dev": true, + "optional": true + }, + "@nx/nx-darwin-x64": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nx/nx-darwin-x64/-/nx-darwin-x64-18.3.4.tgz", + "integrity": "sha512-tSzPRnNB3QdPM+KYiIuRCUtyCwcuIRC95FfP0ZB3WvfDeNxJChEAChNqmCMDE4iFvZhGuze8WqkJuIVdte+lyQ==", + "dev": true, + "optional": true + }, + "@nx/nx-freebsd-x64": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nx/nx-freebsd-x64/-/nx-freebsd-x64-18.3.4.tgz", + "integrity": "sha512-bjSPak/d+bcR95/pxHMRhnnpHc6MnrQcG6f5AjX15Esm4JdrdQKPBmG1RybuK0WKSyD5wgVhkAGc/QQUom9l8g==", + "dev": true, + "optional": true + }, + "@nx/nx-linux-arm-gnueabihf": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-18.3.4.tgz", + "integrity": "sha512-/1HnUL7jhH0S7PxJqf6R1pk3QlAU22GY89EQV9fd+RDUtp7IyzaTlkebijTIqfxlSjC4OO3bPizaxEaxdd3uKQ==", + "dev": true, + "optional": true + }, + "@nx/nx-linux-arm64-gnu": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-18.3.4.tgz", + "integrity": "sha512-g/2IaB2bZTKaBNPEf9LxtIXb1XHdhh3VO9PnePIrwkkixPMLN0dTxT5Sttt75lvLP3EU1AUR5w3Aaz2Q1mYtWA==", + "dev": true, + "optional": true + }, + "@nx/nx-linux-arm64-musl": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-18.3.4.tgz", + "integrity": "sha512-MgfKLoEF6I1cCS+0ooFLEjJSSVdCYyCT9Q96IHRJntAEL8u/0GR2OUoBoLC+q1lnbIkJr/uqTJxA2Jh+sJTIbA==", + "dev": true, + "optional": true + }, + "@nx/nx-linux-x64-gnu": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-18.3.4.tgz", + "integrity": "sha512-vbHxv7m3gjthBvw50EYCtgyY0Zg5nVTaQtX+wRsmKybV2i7wHbw5zIe1aL4zHUm6TcPGbIQK+utVM+hyCqKHVA==", + "dev": true, + "optional": true + }, + "@nx/nx-linux-x64-musl": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-18.3.4.tgz", + "integrity": "sha512-qIJKJCYFRLVSALsvg3avjReOjuYk91Q0hFXMJ2KaEM1Y3tdzcFN0fKBiaHexgbFIUk8zJuS4dJObTqSYMXowbg==", + "dev": true, + "optional": true + }, + "@nx/nx-win32-arm64-msvc": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-18.3.4.tgz", + "integrity": "sha512-UxC8mRkFTPdZbKFprZkiBqVw8624xU38kI0xyooxKlFpt5lccTBwJ0B7+R8p1RoWyvh2DSyFI9VvfD7lczg1lA==", + "dev": true, + "optional": true + }, + "@nx/nx-win32-x64-msvc": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-18.3.4.tgz", + "integrity": "sha512-/RqEjNU9hxIBxRLafCNKoH3SaB2FShf+1ZnIYCdAoCZBxLJebDpnhiyrVs0lPnMj9248JbizEMdJj1+bs/bXig==", + "dev": true, + "optional": true + }, + "@nx/workspace": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nx/workspace/-/workspace-18.3.4.tgz", + "integrity": "sha512-H5HmEOWb9wnrNXfI2DhK6AmMVz1snuJvjT2jcMf9kxlVW0pKGTFW+OyHfSYq6Ni3OGWb1f9O63erLYHo45zPeA==", + "dev": true, + "requires": { + "@nrwl/workspace": "18.3.4", + "@nx/devkit": "18.3.4", + "chalk": "^4.1.0", + "enquirer": "~2.3.6", + "nx": "18.3.4", + "tslib": "^2.3.0", + "yargs-parser": "21.1.1" + } + }, + "@phenomnomnominal/tsquery": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@phenomnomnominal/tsquery/-/tsquery-5.0.1.tgz", + "integrity": "sha512-3nVv+e2FQwsW8Aw6qTU6f+1rfcJ3hrcnvH/mu9i8YhxO+9sqbOfpL8m6PbET5+xKOlz/VSbp0RoYWYCtIsnmuA==", + "dev": true, + "requires": { + "esquery": "^1.4.0" + } + }, + "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 + }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "lines-and-columns": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-2.0.4.tgz", + "integrity": "sha512-wM1+Z03eypVAVUCE7QdSqpVIvelbOakn1M0bPDoA4SGWPx3sNDVUiMo3L6To6WWGClB7VyXnhQ4Sn7gxiJbE6A==", + "dev": true + }, + "minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "nx": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/nx/-/nx-18.3.4.tgz", + "integrity": "sha512-7rOHRyxpnZGJ3pHnwmpoAMHt9hNuwibWhOhPBJDhJVcbQJtGfwcWWyV/iSEnVXwKZ2lfHVE3TwE+gXFdT/GFiw==", + "dev": true, + "requires": { + "@nrwl/tao": "18.3.4", + "@nx/nx-darwin-arm64": "18.3.4", + "@nx/nx-darwin-x64": "18.3.4", + "@nx/nx-freebsd-x64": "18.3.4", + "@nx/nx-linux-arm-gnueabihf": "18.3.4", + "@nx/nx-linux-arm64-gnu": "18.3.4", + "@nx/nx-linux-arm64-musl": "18.3.4", + "@nx/nx-linux-x64-gnu": "18.3.4", + "@nx/nx-linux-x64-musl": "18.3.4", + "@nx/nx-win32-arm64-msvc": "18.3.4", + "@nx/nx-win32-x64-msvc": "18.3.4", + "@yarnpkg/lockfile": "^1.1.0", + "@yarnpkg/parsers": "3.0.0-rc.46", + "@zkochan/js-yaml": "0.0.6", + "axios": "^1.6.0", + "chalk": "^4.1.0", + "cli-cursor": "3.1.0", + "cli-spinners": "2.6.1", + "cliui": "^8.0.1", + "dotenv": "~16.3.1", + "dotenv-expand": "~10.0.0", + "enquirer": "~2.3.6", + "figures": "3.2.0", + "flat": "^5.0.2", + "fs-extra": "^11.1.0", + "ignore": "^5.0.4", + "jest-diff": "^29.4.1", + "js-yaml": "4.1.0", + "jsonc-parser": "3.2.0", + "lines-and-columns": "~2.0.3", + "minimatch": "9.0.3", + "node-machine-id": "1.1.12", + "npm-run-path": "^4.0.1", + "open": "^8.4.0", + "ora": "5.3.0", + "semver": "^7.5.3", + "string-width": "^4.2.3", + "strong-log-transformer": "^2.1.0", + "tar-stream": "~2.2.0", + "tmp": "~0.2.1", + "tsconfig-paths": "^4.1.2", + "tslib": "^2.3.0", + "yargs": "^17.6.2", + "yargs-parser": "21.1.1" + } + }, + "semver": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", + "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true + }, + "tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "requires": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + } + } + }, + "@nx/linter": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/linter/-/linter-18.0.1.tgz", + "integrity": "sha512-RE24Jjdi3R56g9r05hOe37oXx8d5VkVvUC/njgxdaVYy569lgPdiXD9FHaHBXOwjQ3JF7ItVOd2zNhTiZA1S4Q==", + "dev": true, + "requires": { + "@nx/eslint": "18.0.1" + } + }, + "@nx/node": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/node/-/node-18.0.1.tgz", + "integrity": "sha512-SG9RapK6OQsOpv+LdD+7UErmN7jjc4WJHoIRjE3NCJQVJYP2zLaus8kfdSBjF6x+jx/y9fyn/Bxhtznp1IjeLw==", + "dev": true, + "requires": { + "@nrwl/node": "18.0.1", + "@nx/devkit": "18.0.1", + "@nx/eslint": "18.0.1", + "@nx/jest": "18.0.1", + "@nx/js": "18.0.1", + "tslib": "^2.3.0" + }, + "dependencies": { + "@nrwl/js": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nrwl/js/-/js-18.0.1.tgz", + "integrity": "sha512-O1wfFdDMvObgqZgH/5qebm9YeN5cJ+UL2fJV2ZnsWVz3e8jhOQLflN3J7jXUWxzsz5stYiplsGpBjgXQRcNwaQ==", + "dev": true, + "requires": { + "@nx/js": "18.0.1" + } + }, + "@nx/js": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/js/-/js-18.0.1.tgz", + "integrity": "sha512-U/l8K09UMrE4zVXEpmwpRWaY4fCrdcaaB3NXsEUwghJE6OuacADdaQ5ZWPxa0ji3nARKW7Umj87sOlUu8YjpTQ==", + "dev": true, + "requires": { + "@babel/core": "^7.23.2", + "@babel/plugin-proposal-decorators": "^7.22.7", + "@babel/plugin-transform-class-properties": "^7.22.5", + "@babel/plugin-transform-runtime": "^7.23.2", + "@babel/preset-env": "^7.23.2", + "@babel/preset-typescript": "^7.22.5", + "@babel/runtime": "^7.22.6", + "@nrwl/js": "18.0.1", + "@nx/devkit": "18.0.1", + "@nx/workspace": "18.0.1", + "@phenomnomnominal/tsquery": "~5.0.1", + "babel-plugin-const-enum": "^1.0.1", + "babel-plugin-macros": "^2.8.0", + "babel-plugin-transform-typescript-metadata": "^0.3.1", + "chalk": "^4.1.0", + "columnify": "^1.6.0", + "detect-port": "^1.5.1", + "fast-glob": "3.2.7", + "fs-extra": "^11.1.0", + "ignore": "^5.0.4", + "js-tokens": "^4.0.0", + "minimatch": "9.0.3", + "npm-package-arg": "11.0.1", + "npm-run-path": "^4.0.1", + "ora": "5.3.0", + "semver": "^7.5.3", + "source-map-support": "0.5.19", + "ts-node": "10.9.1", + "tsconfig-paths": "^4.1.2", + "tslib": "^2.3.0" + } + }, + "@phenomnomnominal/tsquery": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@phenomnomnominal/tsquery/-/tsquery-5.0.1.tgz", + "integrity": "sha512-3nVv+e2FQwsW8Aw6qTU6f+1rfcJ3hrcnvH/mu9i8YhxO+9sqbOfpL8m6PbET5+xKOlz/VSbp0RoYWYCtIsnmuA==", + "dev": true, + "requires": { + "esquery": "^1.4.0" + } + }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true + }, + "tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "requires": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + } + } + }, + "@nx/nx-darwin-arm64": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/nx-darwin-arm64/-/nx-darwin-arm64-18.0.1.tgz", + "integrity": "sha512-8/fRlTmOLrYyMCwIF/gEU/lYjA5pJ3hVDhmHpCy+VBvCHSJFF2JpFZMOV17XADirlqdlUDiuK1/ZueaTZAUcNg==", + "dev": true, + "optional": true + }, + "@nx/nx-darwin-x64": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/nx-darwin-x64/-/nx-darwin-x64-18.0.1.tgz", + "integrity": "sha512-ejOCxw2wmQimrxrgmsJD+RbblZCnstEfXzLCcqoIgxvuZJjNGvePQJ8INVFq8nFY/imGVFp2YcEiKX5HO6mvjg==", + "dev": true, + "optional": true + }, + "@nx/nx-freebsd-x64": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/nx-freebsd-x64/-/nx-freebsd-x64-18.0.1.tgz", + "integrity": "sha512-Hq64UBjbFN02dLCp12vzNLAx7U94p+NvuV3uh+G1Z9BpCD6FDIgoELY20J37wuz4H7B36vJgXPshWnGEw+XNdA==", + "dev": true, + "optional": true + }, + "@nx/nx-linux-arm-gnueabihf": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-18.0.1.tgz", + "integrity": "sha512-dPC2PTFuumglw8ZbkURRU+eksEAAyV5SyUcFnn1T9Il9+V1F1aZ9yQ0Yjg3YpMBHHkJO2hNgNjHTZp2hMTkW4A==", + "dev": true, + "optional": true + }, + "@nx/nx-linux-arm64-gnu": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-18.0.1.tgz", + "integrity": "sha512-dX5ZF7OAFcE0CnWKOGWWD8yJ9Lz62EdSXafMwZgGWMlgTC1EBF5Rugg224LkCohl9J6Y9JPL6LGNzQsNEDu/oA==", + "dev": true, + "optional": true + }, + "@nx/nx-linux-arm64-musl": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-18.0.1.tgz", + "integrity": "sha512-86jV6SOUdWSvNnQ7QQVeNnSgANpZ+cTJQ4gWnVMkR6DKn2wmneMuChyWuK6I1qTbVMjH6qoclx4zHF51I6S6yQ==", + "dev": true, + "optional": true + }, + "@nx/nx-linux-x64-gnu": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-18.0.1.tgz", + "integrity": "sha512-yvfifJYIJDgaiiWtrpGY7Ggd/wZC61zSunIMmzVyvckDa9NtjtsesYzh05oGy/FHht1avvOKEKrW7VTGOaVNpw==", + "dev": true, + "optional": true + }, + "@nx/nx-linux-x64-musl": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-18.0.1.tgz", + "integrity": "sha512-DZl0pJzla6y6Cr9SWpjLHeSkOefUmt4AC+ag1aqWWO3HSei7dtIBQ2fvM6C26Z8TMD75kXSpCd2nJmJSrb3gYw==", + "dev": true, + "optional": true + }, + "@nx/nx-win32-arm64-msvc": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-18.0.1.tgz", + "integrity": "sha512-fFUmqytVq/jUg6+f0QdnCTyjswNnDKSVeePvsLHU0us6ihmt7QgQS9x/xb41mXFSYLpUSU/ESZhpn7Ao3bdaAQ==", + "dev": true, + "optional": true + }, + "@nx/nx-win32-x64-msvc": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-18.0.1.tgz", + "integrity": "sha512-vCzMpSfJNvAuK2LNW44XVV77GMI8anrVY9XQAFH5TmEYmsHs9NcyPYJL2QfjD/0oiGhtmepd9GfZJvr95SJ3xQ==", + "dev": true, + "optional": true + }, + "@nx/react": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/react/-/react-18.0.1.tgz", + "integrity": "sha512-X6zZF0Nzgiz00LDzHt8paanna7tnwjxnRbOWVc0+TvJ7uOzFJkyThYcwQhK+69d+dQrW+Sot+lQmkrLCT+ilVg==", + "dev": true, + "requires": { + "@nrwl/react": "18.0.1", + "@nx/devkit": "18.0.1", + "@nx/eslint": "18.0.1", + "@nx/js": "18.0.1", + "@nx/web": "18.0.1", + "@phenomnomnominal/tsquery": "~5.0.1", + "@svgr/webpack": "^8.0.1", + "chalk": "^4.1.0", + "minimatch": "9.0.3", + "tslib": "^2.3.0" + }, + "dependencies": { + "@nrwl/js": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nrwl/js/-/js-18.0.1.tgz", + "integrity": "sha512-O1wfFdDMvObgqZgH/5qebm9YeN5cJ+UL2fJV2ZnsWVz3e8jhOQLflN3J7jXUWxzsz5stYiplsGpBjgXQRcNwaQ==", + "dev": true, + "requires": { + "@nx/js": "18.0.1" + } + }, + "@nrwl/web": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nrwl/web/-/web-18.0.1.tgz", + "integrity": "sha512-x6EXhFpgNsLHWQMVilkQBsXI2+otLbTT5TnpwfVUkulNXtA79M6rfnikzt+gQS/vp7l10dXb84Ys2yd4eUyaFQ==", + "dev": true, + "requires": { + "@nx/web": "18.0.1" + } + }, + "@nx/js": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/js/-/js-18.0.1.tgz", + "integrity": "sha512-U/l8K09UMrE4zVXEpmwpRWaY4fCrdcaaB3NXsEUwghJE6OuacADdaQ5ZWPxa0ji3nARKW7Umj87sOlUu8YjpTQ==", + "dev": true, + "requires": { + "@babel/core": "^7.23.2", + "@babel/plugin-proposal-decorators": "^7.22.7", + "@babel/plugin-transform-class-properties": "^7.22.5", + "@babel/plugin-transform-runtime": "^7.23.2", + "@babel/preset-env": "^7.23.2", + "@babel/preset-typescript": "^7.22.5", + "@babel/runtime": "^7.22.6", + "@nrwl/js": "18.0.1", + "@nx/devkit": "18.0.1", + "@nx/workspace": "18.0.1", + "@phenomnomnominal/tsquery": "~5.0.1", + "babel-plugin-const-enum": "^1.0.1", + "babel-plugin-macros": "^2.8.0", + "babel-plugin-transform-typescript-metadata": "^0.3.1", + "chalk": "^4.1.0", + "columnify": "^1.6.0", + "detect-port": "^1.5.1", + "fast-glob": "3.2.7", + "fs-extra": "^11.1.0", + "ignore": "^5.0.4", + "js-tokens": "^4.0.0", + "minimatch": "9.0.3", + "npm-package-arg": "11.0.1", + "npm-run-path": "^4.0.1", + "ora": "5.3.0", + "semver": "^7.5.3", + "source-map-support": "0.5.19", + "ts-node": "10.9.1", + "tsconfig-paths": "^4.1.2", + "tslib": "^2.3.0" + } + }, + "@nx/web": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/web/-/web-18.0.1.tgz", + "integrity": "sha512-F/O/hhbk/tdqbLPfWg+fRVOUVzpmZr6m1tNiHMnK0FPY6r7i66JdhzA/KaaiuH2PD0VJ6gXvkYEauPRvvuOqjQ==", + "dev": true, + "requires": { + "@nrwl/web": "18.0.1", + "@nx/devkit": "18.0.1", + "@nx/js": "18.0.1", + "chalk": "^4.1.0", + "detect-port": "^1.5.1", + "http-server": "^14.1.0", + "tslib": "^2.3.0" + } + }, + "@phenomnomnominal/tsquery": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@phenomnomnominal/tsquery/-/tsquery-5.0.1.tgz", + "integrity": "sha512-3nVv+e2FQwsW8Aw6qTU6f+1rfcJ3hrcnvH/mu9i8YhxO+9sqbOfpL8m6PbET5+xKOlz/VSbp0RoYWYCtIsnmuA==", + "dev": true, + "requires": { + "esquery": "^1.4.0" + } + }, + "@svgr/babel-plugin-add-jsx-attribute": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==", + "dev": true + }, + "@svgr/babel-plugin-replace-jsx-attribute-value": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz", + "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==", + "dev": true + }, + "@svgr/babel-plugin-svg-dynamic-title": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz", + "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==", + "dev": true + }, + "@svgr/babel-plugin-svg-em-dimensions": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz", + "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==", + "dev": true + }, + "@svgr/babel-plugin-transform-react-native-svg": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz", + "integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==", + "dev": true + }, + "@svgr/babel-plugin-transform-svg-component": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz", + "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==", + "dev": true + }, + "@svgr/babel-preset": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz", + "integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==", + "dev": true, + "requires": { + "@svgr/babel-plugin-add-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0", + "@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0", + "@svgr/babel-plugin-svg-dynamic-title": "8.0.0", + "@svgr/babel-plugin-svg-em-dimensions": "8.0.0", + "@svgr/babel-plugin-transform-react-native-svg": "8.1.0", + "@svgr/babel-plugin-transform-svg-component": "8.0.0" + } + }, + "@svgr/core": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", + "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", + "dev": true, + "requires": { + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "camelcase": "^6.2.0", + "cosmiconfig": "^8.1.3", + "snake-case": "^3.0.4" + } + }, + "@svgr/hast-util-to-babel-ast": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz", + "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==", + "dev": true, + "requires": { + "@babel/types": "^7.21.3", + "entities": "^4.4.0" + } + }, + "@svgr/plugin-jsx": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz", + "integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==", + "dev": true, + "requires": { + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "@svgr/hast-util-to-babel-ast": "8.0.0", + "svg-parser": "^2.0.4" + } + }, + "@svgr/plugin-svgo": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-8.1.0.tgz", + "integrity": "sha512-Ywtl837OGO9pTLIN/onoWLmDQ4zFUycI1g76vuKGEz6evR/ZTJlJuz3G/fIkb6OVBJ2g0o6CGJzaEjfmEo3AHA==", + "dev": true, + "requires": { + "cosmiconfig": "^8.1.3", + "deepmerge": "^4.3.1", + "svgo": "^3.0.2" + } + }, + "@svgr/webpack": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-8.1.0.tgz", + "integrity": "sha512-LnhVjMWyMQV9ZmeEy26maJk+8HTIbd59cH4F2MJ439k9DqejRisfFNGAPvRYlKETuh9LrImlS8aKsBgKjMA8WA==", + "dev": true, + "requires": { + "@babel/core": "^7.21.3", + "@babel/plugin-transform-react-constant-elements": "^7.21.3", + "@babel/preset-env": "^7.20.2", + "@babel/preset-react": "^7.18.6", + "@babel/preset-typescript": "^7.21.0", + "@svgr/core": "8.1.0", + "@svgr/plugin-jsx": "8.1.0", + "@svgr/plugin-svgo": "8.1.0" + } + }, + "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 + }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true + }, + "commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true + }, + "cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "dev": true, + "requires": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + } + }, + "css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "dev": true, + "requires": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + } + }, + "css-tree": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "dev": true, + "requires": { + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" + } + }, + "csso": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", + "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", + "dev": true, + "requires": { + "css-tree": "~2.2.0" + }, + "dependencies": { + "css-tree": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", + "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", + "dev": true, + "requires": { + "mdn-data": "2.0.28", + "source-map-js": "^1.0.1" + } + }, + "mdn-data": { + "version": "2.0.28", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", + "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", + "dev": true + } + } + }, + "dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "requires": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + } + }, + "domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "requires": { + "domelementtype": "^2.3.0" + } + }, + "domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "dev": true, + "requires": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + } + }, + "entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true + }, + "fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", + "dev": true + }, + "minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true + }, + "svgo": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.2.0.tgz", + "integrity": "sha512-4PP6CMW/V7l/GmKRKzsLR8xxjdHTV4IMvhTnpuHwwBazSIlw5W/5SmPjN8Dwyt7lKbSJrRDgp4t9ph0HgChFBQ==", + "dev": true, + "requires": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^5.1.0", + "css-tree": "^2.3.1", + "css-what": "^6.1.0", + "csso": "^5.0.5", + "picocolors": "^1.0.0" + } + }, + "tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "requires": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + } + } + }, + "@nx/rollup": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/rollup/-/rollup-18.0.1.tgz", + "integrity": "sha512-VEAGyLV+1NLtPCgO03JwPuWl2kgGrcGA3/QiDMfvBkzXOXr2dY3rZd9qiGmDMeKXhQX6mI9QghIF8SWQWP/kSw==", + "dev": true, + "requires": { + "@nrwl/rollup": "18.0.1", + "@nx/devkit": "18.0.1", + "@nx/js": "18.0.1", + "@rollup/plugin-babel": "^5.3.0", + "@rollup/plugin-commonjs": "^20.0.0", + "@rollup/plugin-image": "^2.1.0", + "@rollup/plugin-json": "^4.1.0", + "@rollup/plugin-node-resolve": "^13.0.4", + "autoprefixer": "^10.4.9", + "babel-plugin-transform-async-to-promises": "^0.8.15", + "chalk": "^4.1.0", + "postcss": "^8.4.14", + "rollup": "^2.56.2", + "rollup-plugin-copy": "^3.4.0", + "rollup-plugin-peer-deps-external": "^2.2.4", + "rollup-plugin-postcss": "^4.0.1", + "rollup-plugin-typescript2": "0.34.1", + "rxjs": "^7.8.0", + "tslib": "^2.3.0" + }, + "dependencies": { + "@nrwl/js": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nrwl/js/-/js-18.0.1.tgz", + "integrity": "sha512-O1wfFdDMvObgqZgH/5qebm9YeN5cJ+UL2fJV2ZnsWVz3e8jhOQLflN3J7jXUWxzsz5stYiplsGpBjgXQRcNwaQ==", + "dev": true, + "requires": { + "@nx/js": "18.0.1" + } + }, + "@nx/js": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/js/-/js-18.0.1.tgz", + "integrity": "sha512-U/l8K09UMrE4zVXEpmwpRWaY4fCrdcaaB3NXsEUwghJE6OuacADdaQ5ZWPxa0ji3nARKW7Umj87sOlUu8YjpTQ==", + "dev": true, + "requires": { + "@babel/core": "^7.23.2", + "@babel/plugin-proposal-decorators": "^7.22.7", + "@babel/plugin-transform-class-properties": "^7.22.5", + "@babel/plugin-transform-runtime": "^7.23.2", + "@babel/preset-env": "^7.23.2", + "@babel/preset-typescript": "^7.22.5", + "@babel/runtime": "^7.22.6", + "@nrwl/js": "18.0.1", + "@nx/devkit": "18.0.1", + "@nx/workspace": "18.0.1", + "@phenomnomnominal/tsquery": "~5.0.1", + "babel-plugin-const-enum": "^1.0.1", + "babel-plugin-macros": "^2.8.0", + "babel-plugin-transform-typescript-metadata": "^0.3.1", + "chalk": "^4.1.0", + "columnify": "^1.6.0", + "detect-port": "^1.5.1", + "fast-glob": "3.2.7", + "fs-extra": "^11.1.0", + "ignore": "^5.0.4", + "js-tokens": "^4.0.0", + "minimatch": "9.0.3", + "npm-package-arg": "11.0.1", + "npm-run-path": "^4.0.1", + "ora": "5.3.0", + "semver": "^7.5.3", + "source-map-support": "0.5.19", + "ts-node": "10.9.1", + "tsconfig-paths": "^4.1.2", + "tslib": "^2.3.0" + } + }, + "@phenomnomnominal/tsquery": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@phenomnomnominal/tsquery/-/tsquery-5.0.1.tgz", + "integrity": "sha512-3nVv+e2FQwsW8Aw6qTU6f+1rfcJ3hrcnvH/mu9i8YhxO+9sqbOfpL8m6PbET5+xKOlz/VSbp0RoYWYCtIsnmuA==", + "dev": true, + "requires": { + "esquery": "^1.4.0" + } + }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true + }, + "tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "requires": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + } + } + }, + "@nx/vite": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nx/vite/-/vite-18.3.4.tgz", + "integrity": "sha512-ZEynabx+GVjUF7t3I4VlY5ivXA2mOTQ4t+pjN9SNs9JdnLTMK/b/rErkNV7Bn2oQK56Ks74Tfbx9r7Dc5S4exg==", + "dev": true, + "requires": { + "@nrwl/vite": "18.3.4", + "@nx/devkit": "18.3.4", + "@nx/js": "18.3.4", + "@phenomnomnominal/tsquery": "~5.0.1", + "@swc/helpers": "~0.5.0", + "enquirer": "~2.3.6", + "tsconfig-paths": "^4.1.2" + }, + "dependencies": { + "@nrwl/devkit": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nrwl/devkit/-/devkit-18.3.4.tgz", + "integrity": "sha512-Fty9Huqm12OYueU3uLJl3uvBUl5BvEyPfvw8+rLiNx9iftdEattM8C+268eAbIRRSLSOVXlWsJH4brlc6QZYYw==", + "dev": true, + "requires": { + "@nx/devkit": "18.3.4" + } + }, + "@nx/devkit": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nx/devkit/-/devkit-18.3.4.tgz", + "integrity": "sha512-M3htxl5WvlNKK5KNOndCAApbyBCZNTFFs+rtdwvudNZk5+84zAAPaWzSoX9C4XLAW78/f98LzF68/ch05aN12A==", + "dev": true, + "requires": { + "@nrwl/devkit": "18.3.4", + "ejs": "^3.1.7", + "enquirer": "~2.3.6", + "ignore": "^5.0.4", + "semver": "^7.5.3", + "tmp": "~0.2.1", + "tslib": "^2.3.0", + "yargs-parser": "21.1.1" + } + }, + "@phenomnomnominal/tsquery": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@phenomnomnominal/tsquery/-/tsquery-5.0.1.tgz", + "integrity": "sha512-3nVv+e2FQwsW8Aw6qTU6f+1rfcJ3hrcnvH/mu9i8YhxO+9sqbOfpL8m6PbET5+xKOlz/VSbp0RoYWYCtIsnmuA==", + "dev": true, + "requires": { + "esquery": "^1.4.0" + } + }, + "semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true + }, + "tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "requires": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + } + } + }, + "@nx/web": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nx/web/-/web-18.3.4.tgz", + "integrity": "sha512-d+BCmJyELI2JXMtqnF7re8g/98pEyej8gUhL7ewywZ/LMEWJ8toG7yrEZoA6o2tlO3Fn63+7kUun7s5ZpkRElA==", + "dev": true, + "requires": { + "@nrwl/web": "18.3.4", + "@nx/devkit": "18.3.4", + "@nx/js": "18.3.4", + "chalk": "^4.1.0", + "detect-port": "^1.5.1", + "http-server": "^14.1.0", + "tslib": "^2.3.0" + }, + "dependencies": { + "@nrwl/devkit": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nrwl/devkit/-/devkit-18.3.4.tgz", + "integrity": "sha512-Fty9Huqm12OYueU3uLJl3uvBUl5BvEyPfvw8+rLiNx9iftdEattM8C+268eAbIRRSLSOVXlWsJH4brlc6QZYYw==", + "dev": true, + "requires": { + "@nx/devkit": "18.3.4" + } + }, + "@nx/devkit": { + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@nx/devkit/-/devkit-18.3.4.tgz", + "integrity": "sha512-M3htxl5WvlNKK5KNOndCAApbyBCZNTFFs+rtdwvudNZk5+84zAAPaWzSoX9C4XLAW78/f98LzF68/ch05aN12A==", + "dev": true, + "requires": { + "@nrwl/devkit": "18.3.4", + "ejs": "^3.1.7", + "enquirer": "~2.3.6", + "ignore": "^5.0.4", + "semver": "^7.5.3", + "tmp": "~0.2.1", + "tslib": "^2.3.0", + "yargs-parser": "21.1.1" + } + }, + "semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@nx/webpack": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/webpack/-/webpack-18.0.1.tgz", + "integrity": "sha512-W5/kO5LRGn4iWxZmXc3OeSCVnhCqD65lIMmsqZN3t25XLsaGSKjhUUWyf6G4zV29K0orkhZbqGmDyJqEnnOXbg==", + "dev": true, + "requires": { + "@babel/core": "^7.23.2", + "@nrwl/webpack": "18.0.1", + "@nx/devkit": "18.0.1", + "@nx/js": "18.0.1", + "autoprefixer": "^10.4.9", + "babel-loader": "^9.1.2", + "browserslist": "^4.21.4", + "chalk": "^4.1.0", + "copy-webpack-plugin": "^10.2.4", + "css-loader": "^6.4.0", + "css-minimizer-webpack-plugin": "^5.0.0", + "fork-ts-checker-webpack-plugin": "7.2.13", + "less": "4.1.3", + "less-loader": "11.1.0", + "license-webpack-plugin": "^4.0.2", + "loader-utils": "^2.0.3", + "mini-css-extract-plugin": "~2.4.7", + "parse5": "4.0.0", + "postcss": "^8.4.14", + "postcss-import": "~14.1.0", + "postcss-loader": "^6.1.1", + "rxjs": "^7.8.0", + "sass": "^1.42.1", + "sass-loader": "^12.2.0", + "source-map-loader": "^3.0.0", + "style-loader": "^3.3.0", + "stylus": "^0.59.0", + "stylus-loader": "^7.1.0", + "terser-webpack-plugin": "^5.3.3", + "ts-loader": "^9.3.1", + "tsconfig-paths-webpack-plugin": "4.0.0", + "tslib": "^2.3.0", + "webpack": "^5.80.0", + "webpack-dev-server": "^4.9.3", + "webpack-node-externals": "^3.0.0", + "webpack-subresource-integrity": "^5.1.0" + }, + "dependencies": { + "@nrwl/js": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nrwl/js/-/js-18.0.1.tgz", + "integrity": "sha512-O1wfFdDMvObgqZgH/5qebm9YeN5cJ+UL2fJV2ZnsWVz3e8jhOQLflN3J7jXUWxzsz5stYiplsGpBjgXQRcNwaQ==", + "dev": true, + "requires": { + "@nx/js": "18.0.1" + } + }, + "@nx/js": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/js/-/js-18.0.1.tgz", + "integrity": "sha512-U/l8K09UMrE4zVXEpmwpRWaY4fCrdcaaB3NXsEUwghJE6OuacADdaQ5ZWPxa0ji3nARKW7Umj87sOlUu8YjpTQ==", + "dev": true, + "requires": { + "@babel/core": "^7.23.2", + "@babel/plugin-proposal-decorators": "^7.22.7", + "@babel/plugin-transform-class-properties": "^7.22.5", + "@babel/plugin-transform-runtime": "^7.23.2", + "@babel/preset-env": "^7.23.2", + "@babel/preset-typescript": "^7.22.5", + "@babel/runtime": "^7.22.6", + "@nrwl/js": "18.0.1", + "@nx/devkit": "18.0.1", + "@nx/workspace": "18.0.1", + "@phenomnomnominal/tsquery": "~5.0.1", + "babel-plugin-const-enum": "^1.0.1", + "babel-plugin-macros": "^2.8.0", + "babel-plugin-transform-typescript-metadata": "^0.3.1", + "chalk": "^4.1.0", + "columnify": "^1.6.0", + "detect-port": "^1.5.1", + "fast-glob": "3.2.7", + "fs-extra": "^11.1.0", + "ignore": "^5.0.4", + "js-tokens": "^4.0.0", + "minimatch": "9.0.3", + "npm-package-arg": "11.0.1", + "npm-run-path": "^4.0.1", + "ora": "5.3.0", + "semver": "^7.5.3", + "source-map-support": "0.5.19", + "ts-node": "10.9.1", + "tsconfig-paths": "^4.1.2", + "tslib": "^2.3.0" + } + }, + "@phenomnomnominal/tsquery": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@phenomnomnominal/tsquery/-/tsquery-5.0.1.tgz", + "integrity": "sha512-3nVv+e2FQwsW8Aw6qTU6f+1rfcJ3hrcnvH/mu9i8YhxO+9sqbOfpL8m6PbET5+xKOlz/VSbp0RoYWYCtIsnmuA==", + "dev": true, + "requires": { + "esquery": "^1.4.0" + } + }, + "ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "babel-loader": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.1.3.tgz", + "integrity": "sha512-xG3ST4DglodGf8qSwv0MdeWLhrDsw/32QMdTO5T1ZIp9gQur0HkCyFs7Awskr10JKXFXwpAhiCuYX5oGXnRGbw==", + "dev": true, + "requires": { + "find-cache-dir": "^4.0.0", + "schema-utils": "^4.0.0" + } + }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "find-cache-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-4.0.0.tgz", + "integrity": "sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==", + "dev": true, + "requires": { + "common-path-prefix": "^3.0.0", + "pkg-dir": "^7.0.0" + } + }, + "find-up": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", + "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", + "dev": true, + "requires": { + "locate-path": "^7.1.0", + "path-exists": "^5.0.0" + } + }, + "fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "locate-path": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", + "dev": true, + "requires": { + "p-locate": "^6.0.0" + } + }, + "minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "dev": true, + "requires": { + "yocto-queue": "^1.0.0" + } + }, + "p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "dev": true, + "requires": { + "p-limit": "^4.0.0" + } + }, + "parse5": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", + "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", + "dev": true + }, + "path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "dev": true + }, + "pkg-dir": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-7.0.0.tgz", + "integrity": "sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==", + "dev": true, + "requires": { + "find-up": "^6.3.0" + } + }, + "schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + } + }, + "semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true + }, + "tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "requires": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "yocto-queue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", + "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "dev": true + } + } + }, + "@nx/workspace": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@nx/workspace/-/workspace-18.0.1.tgz", + "integrity": "sha512-yxURsmyx0XuXdxm/IoeM05CKdHepq2YGJjZISofMyqZG22Gr/TR2ygU2JxSgeuOdEkEHsEp11SSqFwb8CSVYZw==", + "dev": true, + "requires": { + "@nrwl/workspace": "18.0.1", + "@nx/devkit": "18.0.1", + "chalk": "^4.1.0", + "enquirer": "~2.3.6", + "nx": "18.0.1", + "tslib": "^2.3.0", + "yargs-parser": "21.1.1" + } + }, + "@phenomnomnominal/tsquery": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@phenomnomnominal/tsquery/-/tsquery-4.1.1.tgz", + "integrity": "sha512-jjMmK1tnZbm1Jq5a7fBliM4gQwjxMU7TFoRNwIyzwlO+eHPRCFv/Nv+H/Gi1jc3WR7QURG8D5d0Tn12YGrUqBQ==", + "dev": true, + "requires": { + "esquery": "^1.0.1" + } + }, + "@polka/url": { + "version": "1.0.0-next.25", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.25.tgz", + "integrity": "sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==" + }, + "@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "requires": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, + "@rollup/plugin-babel": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", + "integrity": "sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.10.4", + "@rollup/pluginutils": "^3.1.0" + } + }, + "@rollup/plugin-commonjs": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-20.0.0.tgz", + "integrity": "sha512-5K0g5W2Ol8hAcTHqcTBHiA7M58tfmYi1o9KxeJuuRNpGaTa5iLjcyemBitCBcKXaHamOBBEH2dGom6v6Unmqjg==", + "dev": true, + "requires": { + "@rollup/pluginutils": "^3.1.0", + "commondir": "^1.0.1", + "estree-walker": "^2.0.1", + "glob": "^7.1.6", + "is-reference": "^1.2.1", + "magic-string": "^0.25.7", + "resolve": "^1.17.0" + } + }, + "@rollup/plugin-image": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-image/-/plugin-image-2.1.1.tgz", + "integrity": "sha512-AgP4U85zuQJdUopLUCM+hTf45RepgXeTb8EJsleExVy99dIoYpt3ZlDYJdKmAc2KLkNntCDg6BPJvgJU3uGF+g==", + "dev": true, + "requires": { + "@rollup/pluginutils": "^3.1.0", + "mini-svg-data-uri": "^1.2.3" + } + }, + "@rollup/plugin-json": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-4.1.0.tgz", + "integrity": "sha512-yfLbTdNS6amI/2OpmbiBoW12vngr5NW2jCJVZSBEz+H5KfUJZ2M7sDjk0U6GOOdCWFVScShte29o9NezJ53TPw==", + "dev": true, + "requires": { + "@rollup/pluginutils": "^3.0.8" + } + }, + "@rollup/plugin-node-resolve": { + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.3.0.tgz", + "integrity": "sha512-Lus8rbUo1eEcnS4yTFKLZrVumLPY+YayBdWXgFSHYhTT2iJbMhoaaBL3xl5NCdeRytErGr8tZ0L71BMRmnlwSw==", + "dev": true, + "requires": { + "@rollup/pluginutils": "^3.1.0", + "@types/resolve": "1.17.1", + "deepmerge": "^4.2.2", + "is-builtin-module": "^3.1.0", + "is-module": "^1.0.0", + "resolve": "^1.19.0" + } + }, + "@rollup/pluginutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", + "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", + "dev": true, + "requires": { + "@types/estree": "0.0.39", + "estree-walker": "^1.0.1", + "picomatch": "^2.2.2" + }, + "dependencies": { + "@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", + "dev": true + }, + "estree-walker": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", + "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", + "dev": true + } + } + }, + "@rollup/rollup-android-arm-eabi": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.17.2.tgz", + "integrity": "sha512-NM0jFxY8bB8QLkoKxIQeObCaDlJKewVlIEkuyYKm5An1tdVZ966w2+MPQ2l8LBZLjR+SgyV+nRkTIunzOYBMLQ==", + "dev": true, + "optional": true + }, + "@rollup/rollup-android-arm64": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.17.2.tgz", + "integrity": "sha512-yeX/Usk7daNIVwkq2uGoq2BYJKZY1JfyLTaHO/jaiSwi/lsf8fTFoQW/n6IdAsx5tx+iotu2zCJwz8MxI6D/Bw==", + "dev": true, + "optional": true + }, + "@rollup/rollup-darwin-arm64": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.17.2.tgz", + "integrity": "sha512-kcMLpE6uCwls023+kknm71ug7MZOrtXo+y5p/tsg6jltpDtgQY1Eq5sGfHcQfb+lfuKwhBmEURDga9N0ol4YPw==", + "dev": true, + "optional": true + }, + "@rollup/rollup-darwin-x64": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.17.2.tgz", + "integrity": "sha512-AtKwD0VEx0zWkL0ZjixEkp5tbNLzX+FCqGG1SvOu993HnSz4qDI6S4kGzubrEJAljpVkhRSlg5bzpV//E6ysTQ==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.17.2.tgz", + "integrity": "sha512-3reX2fUHqN7sffBNqmEyMQVj/CKhIHZd4y631duy0hZqI8Qoqf6lTtmAKvJFYa6bhU95B1D0WgzHkmTg33In0A==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-arm-musleabihf": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.17.2.tgz", + "integrity": "sha512-uSqpsp91mheRgw96xtyAGP9FW5ChctTFEoXP0r5FAzj/3ZRv3Uxjtc7taRQSaQM/q85KEKjKsZuiZM3GyUivRg==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-arm64-gnu": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.17.2.tgz", + "integrity": "sha512-EMMPHkiCRtE8Wdk3Qhtciq6BndLtstqZIroHiiGzB3C5LDJmIZcSzVtLRbwuXuUft1Cnv+9fxuDtDxz3k3EW2A==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-arm64-musl": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.17.2.tgz", + "integrity": "sha512-NMPylUUZ1i0z/xJUIx6VUhISZDRT+uTWpBcjdv0/zkp7b/bQDF+NfnfdzuTiB1G6HTodgoFa93hp0O1xl+/UbA==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.17.2.tgz", + "integrity": "sha512-T19My13y8uYXPw/L/k0JYaX1fJKFT/PWdXiHr8mTbXWxjVF1t+8Xl31DgBBvEKclw+1b00Chg0hxE2O7bTG7GQ==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-riscv64-gnu": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.17.2.tgz", + "integrity": "sha512-BOaNfthf3X3fOWAB+IJ9kxTgPmMqPPH5f5k2DcCsRrBIbWnaJCgX2ll77dV1TdSy9SaXTR5iDXRL8n7AnoP5cg==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-s390x-gnu": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.17.2.tgz", + "integrity": "sha512-W0UP/x7bnn3xN2eYMql2T/+wpASLE5SjObXILTMPUBDB/Fg/FxC+gX4nvCfPBCbNhz51C+HcqQp2qQ4u25ok6g==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-x64-gnu": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.17.2.tgz", + "integrity": "sha512-Hy7pLwByUOuyaFC6mAr7m+oMC+V7qyifzs/nW2OJfC8H4hbCzOX07Ov0VFk/zP3kBsELWNFi7rJtgbKYsav9QQ==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-x64-musl": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.17.2.tgz", + "integrity": "sha512-h1+yTWeYbRdAyJ/jMiVw0l6fOOm/0D1vNLui9iPuqgRGnXA0u21gAqOyB5iHjlM9MMfNOm9RHCQ7zLIzT0x11Q==", + "dev": true, + "optional": true + }, + "@rollup/rollup-win32-arm64-msvc": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.17.2.tgz", + "integrity": "sha512-tmdtXMfKAjy5+IQsVtDiCfqbynAQE/TQRpWdVataHmhMb9DCoJxp9vLcCBjEQWMiUYxO1QprH/HbY9ragCEFLA==", + "dev": true, + "optional": true + }, + "@rollup/rollup-win32-ia32-msvc": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.17.2.tgz", + "integrity": "sha512-7II/QCSTAHuE5vdZaQEwJq2ZACkBpQDOmQsE6D6XUbnBHW8IAhm4eTufL6msLJorzrHDFv3CF8oCA/hSIRuZeQ==", + "dev": true, + "optional": true + }, + "@rollup/rollup-win32-x64-msvc": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.17.2.tgz", + "integrity": "sha512-TGGO7v7qOq4CYmSBVEYpI1Y5xDuCEnbVC5Vth8mOsW0gDSzxNrVERPc790IGHsrT2dQSimgMr9Ub3Y1Jci5/8w==", + "dev": true, + "optional": true + }, + "@rushstack/eslint-patch": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz", + "integrity": "sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==", + "dev": true + }, + "@sideway/address": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz", + "integrity": "sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==", + "requires": { + "@hapi/hoek": "^9.0.0" + } + }, + "@sideway/formula": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==" + }, + "@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" + }, + "@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==" + }, + "@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "requires": { + "@sinonjs/commons": "^3.0.0" + } + }, + "@slorber/static-site-generator-webpack-plugin": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@slorber/static-site-generator-webpack-plugin/-/static-site-generator-webpack-plugin-4.0.7.tgz", + "integrity": "sha512-Ug7x6z5lwrz0WqdnNFOMYrDQNTPAprvHLSh6+/fmml3qUiz6l5eq+2MzLKWtn/q5K5NpSiFsZTP/fck/3vjSxA==", + "requires": { + "eval": "^0.1.8", + "p-map": "^4.0.0", + "webpack-sources": "^3.2.2" + } + }, + "@svgr/babel-plugin-add-jsx-attribute": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-6.5.1.tgz", + "integrity": "sha512-9PYGcXrAxitycIjRmZB+Q0JaN07GZIWaTBIGQzfaZv+qr1n8X1XUEJ5rZ/vx6OVD9RRYlrNnXWExQXcmZeD/BQ==" + }, + "@svgr/babel-plugin-remove-jsx-attribute": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==" + }, + "@svgr/babel-plugin-remove-jsx-empty-expression": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz", + "integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==" + }, + "@svgr/babel-plugin-replace-jsx-attribute-value": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-6.5.1.tgz", + "integrity": "sha512-8DPaVVE3fd5JKuIC29dqyMB54sA6mfgki2H2+swh+zNJoynC8pMPzOkidqHOSc6Wj032fhl8Z0TVn1GiPpAiJg==" + }, + "@svgr/babel-plugin-svg-dynamic-title": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-6.5.1.tgz", + "integrity": "sha512-FwOEi0Il72iAzlkaHrlemVurgSQRDFbk0OC8dSvD5fSBPHltNh7JtLsxmZUhjYBZo2PpcU/RJvvi6Q0l7O7ogw==" + }, + "@svgr/babel-plugin-svg-em-dimensions": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-6.5.1.tgz", + "integrity": "sha512-gWGsiwjb4tw+ITOJ86ndY/DZZ6cuXMNE/SjcDRg+HLuCmwpcjOktwRF9WgAiycTqJD/QXqL2f8IzE2Rzh7aVXA==" + }, + "@svgr/babel-plugin-transform-react-native-svg": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-6.5.1.tgz", + "integrity": "sha512-2jT3nTayyYP7kI6aGutkyfJ7UMGtuguD72OjeGLwVNyfPRBD8zQthlvL+fAbAKk5n9ZNcvFkp/b1lZ7VsYqVJg==" + }, + "@svgr/babel-plugin-transform-svg-component": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-6.5.1.tgz", + "integrity": "sha512-a1p6LF5Jt33O3rZoVRBqdxL350oge54iZWHNI6LJB5tQ7EelvD/Mb1mfBiZNAan0dt4i3VArkFRjA4iObuNykQ==" + }, + "@svgr/babel-preset": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-6.5.1.tgz", + "integrity": "sha512-6127fvO/FF2oi5EzSQOAjo1LE3OtNVh11R+/8FXa+mHx1ptAaS4cknIjnUA7e6j6fwGGJ17NzaTJFUwOV2zwCw==", + "requires": { + "@svgr/babel-plugin-add-jsx-attribute": "^6.5.1", + "@svgr/babel-plugin-remove-jsx-attribute": "*", + "@svgr/babel-plugin-remove-jsx-empty-expression": "*", + "@svgr/babel-plugin-replace-jsx-attribute-value": "^6.5.1", + "@svgr/babel-plugin-svg-dynamic-title": "^6.5.1", + "@svgr/babel-plugin-svg-em-dimensions": "^6.5.1", + "@svgr/babel-plugin-transform-react-native-svg": "^6.5.1", + "@svgr/babel-plugin-transform-svg-component": "^6.5.1" + } + }, + "@svgr/core": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-6.5.1.tgz", + "integrity": "sha512-/xdLSWxK5QkqG524ONSjvg3V/FkNyCv538OIBdQqPNaAta3AsXj/Bd2FbvR87yMbXO2hFSWiAe/Q6IkVPDw+mw==", + "requires": { + "@babel/core": "^7.19.6", + "@svgr/babel-preset": "^6.5.1", + "@svgr/plugin-jsx": "^6.5.1", + "camelcase": "^6.2.0", + "cosmiconfig": "^7.0.1" + }, + "dependencies": { + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==" + } + } + }, + "@svgr/hast-util-to-babel-ast": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-6.5.1.tgz", + "integrity": "sha512-1hnUxxjd83EAxbL4a0JDJoD3Dao3hmjvyvyEV8PzWmLK3B9m9NPlW7GKjFyoWE8nM7HnXzPcmmSyOW8yOddSXw==", + "requires": { + "@babel/types": "^7.20.0", + "entities": "^4.4.0" + }, + "dependencies": { + "entities": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", + "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==" + } + } + }, + "@svgr/plugin-jsx": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-6.5.1.tgz", + "integrity": "sha512-+UdQxI3jgtSjCykNSlEMuy1jSRQlGC7pqBCPvkG/2dATdWo082zHTTK3uhnAju2/6XpE6B5mZ3z4Z8Ns01S8Gw==", + "requires": { + "@babel/core": "^7.19.6", + "@svgr/babel-preset": "^6.5.1", + "@svgr/hast-util-to-babel-ast": "^6.5.1", + "svg-parser": "^2.0.4" + } + }, + "@svgr/plugin-svgo": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-6.5.1.tgz", + "integrity": "sha512-omvZKf8ixP9z6GWgwbtmP9qQMPX4ODXi+wzbVZgomNFsUIlHA1sf4fThdwTWSsZGgvGAG6yE+b/F5gWUkcZ/iQ==", + "requires": { + "cosmiconfig": "^7.0.1", + "deepmerge": "^4.2.2", + "svgo": "^2.8.0" + } + }, + "@svgr/webpack": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-6.5.1.tgz", + "integrity": "sha512-cQ/AsnBkXPkEK8cLbv4Dm7JGXq2XrumKnL1dRpJD9rIO2fTIlJI9a1uCciYG1F2aUsox/hJQyNGbt3soDxSRkA==", + "requires": { + "@babel/core": "^7.19.6", + "@babel/plugin-transform-react-constant-elements": "^7.18.12", + "@babel/preset-env": "^7.19.4", + "@babel/preset-react": "^7.18.6", + "@babel/preset-typescript": "^7.18.6", + "@svgr/core": "^6.5.1", + "@svgr/plugin-jsx": "^6.5.1", + "@svgr/plugin-svgo": "^6.5.1" + } + }, + "@swc-node/core": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/@swc-node/core/-/core-1.13.1.tgz", + "integrity": "sha512-emB5l2nZsXjUEAuusqjYvWnQMLWZp6K039Mv8aq5SX1rsNM/N7DNhw1i4/DX7AyzNZ0tT+ASWyTvqEURldp5HA==", + "dev": true + }, + "@swc-node/register": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@swc-node/register/-/register-1.8.0.tgz", + "integrity": "sha512-8K3589HoBSmVmrEVrtr4K5sWEithpGDzcFGic81OW0A9sZY38IV5EGRODQWCk0SBDyLhaF+pid120vJAtsHo1A==", + "dev": true, + "requires": { + "@swc-node/core": "^1.12.0", + "@swc-node/sourcemap-support": "^0.4.0", + "colorette": "^2.0.20", + "debug": "^4.3.4", + "pirates": "^4.0.6", + "tslib": "^2.6.2" + } + }, + "@swc-node/sourcemap-support": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@swc-node/sourcemap-support/-/sourcemap-support-0.4.0.tgz", + "integrity": "sha512-weuRmYTO+4yOtHtPZHXlPdA1dJJJp3QOoZAFZ6uZidu992F2X5v1fQdnb26xs1o3Ex/e2sYhRyY5R6NGNuoATQ==", + "dev": true, + "requires": { + "source-map-support": "^0.5.21", + "tslib": "^2.6.2" + }, + "dependencies": { + "source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + } + } + }, + "@swc/core": { + "version": "1.3.107", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.107.tgz", + "integrity": "sha512-zKhqDyFcTsyLIYK1iEmavljZnf4CCor5pF52UzLAz4B6Nu/4GLU+2LQVAf+oRHjusG39PTPjd2AlRT3f3QWfsQ==", + "dev": true, + "requires": { + "@swc/core-darwin-arm64": "1.3.107", + "@swc/core-darwin-x64": "1.3.107", + "@swc/core-linux-arm-gnueabihf": "1.3.107", + "@swc/core-linux-arm64-gnu": "1.3.107", + "@swc/core-linux-arm64-musl": "1.3.107", + "@swc/core-linux-x64-gnu": "1.3.107", + "@swc/core-linux-x64-musl": "1.3.107", + "@swc/core-win32-arm64-msvc": "1.3.107", + "@swc/core-win32-ia32-msvc": "1.3.107", + "@swc/core-win32-x64-msvc": "1.3.107", + "@swc/counter": "^0.1.1", + "@swc/types": "^0.1.5" + } + }, + "@swc/core-darwin-arm64": { + "version": "1.3.107", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.107.tgz", + "integrity": "sha512-47tD/5vSXWxPd0j/ZllyQUg4bqalbQTsmqSw0J4dDdS82MWqCAwUErUrAZPRjBkjNQ6Kmrf5rpCWaGTtPw+ngw==", + "dev": true, + "optional": true + }, + "@swc/core-darwin-x64": { + "version": "1.3.107", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.107.tgz", + "integrity": "sha512-hwiLJ2ulNkBGAh1m1eTfeY1417OAYbRGcb/iGsJ+LuVLvKAhU/itzsl535CvcwAlt2LayeCFfcI8gdeOLeZa9A==", + "dev": true, + "optional": true + }, + "@swc/core-linux-arm-gnueabihf": { + "version": "1.3.107", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.107.tgz", + "integrity": "sha512-I2wzcC0KXqh0OwymCmYwNRgZ9nxX7DWnOOStJXV3pS0uB83TXAkmqd7wvMBuIl9qu4Hfomi9aDM7IlEEn9tumQ==", + "dev": true, + "optional": true + }, + "@swc/core-linux-arm64-gnu": { + "version": "1.3.107", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.107.tgz", + "integrity": "sha512-HWgnn7JORYlOYnGsdunpSF8A+BCZKPLzLtEUA27/M/ZuANcMZabKL9Zurt7XQXq888uJFAt98Gy+59PU90aHKg==", + "dev": true, + "optional": true + }, + "@swc/core-linux-arm64-musl": { + "version": "1.3.107", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.107.tgz", + "integrity": "sha512-vfPF74cWfAm8hyhS8yvYI94ucMHIo8xIYU+oFOW9uvDlGQRgnUf/6DEVbLyt/3yfX5723Ln57U8uiMALbX5Pyw==", + "dev": true, + "optional": true + }, + "@swc/core-linux-x64-gnu": { + "version": "1.3.107", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.107.tgz", + "integrity": "sha512-uBVNhIg0ip8rH9OnOsCARUFZ3Mq3tbPHxtmWk9uAa5u8jQwGWeBx5+nTHpDOVd3YxKb6+5xDEI/edeeLpha/9g==", + "dev": true, + "optional": true + }, + "@swc/core-linux-x64-musl": { + "version": "1.3.107", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.107.tgz", + "integrity": "sha512-mvACkUvzSIB12q1H5JtabWATbk3AG+pQgXEN95AmEX2ZA5gbP9+B+mijsg7Sd/3tboHr7ZHLz/q3SHTvdFJrEw==", + "dev": true, + "optional": true + }, + "@swc/core-win32-arm64-msvc": { + "version": "1.3.107", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.107.tgz", + "integrity": "sha512-J3P14Ngy/1qtapzbguEH41kY109t6DFxfbK4Ntz9dOWNuVY3o9/RTB841ctnJk0ZHEG+BjfCJjsD2n8H5HcaOA==", + "dev": true, + "optional": true + }, + "@swc/core-win32-ia32-msvc": { + "version": "1.3.107", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.107.tgz", + "integrity": "sha512-ZBUtgyjTHlz8TPJh7kfwwwFma+ktr6OccB1oXC8fMSopD0AxVnQasgun3l3099wIsAB9eEsJDQ/3lDkOLs1gBA==", + "dev": true, + "optional": true + }, + "@swc/core-win32-x64-msvc": { + "version": "1.3.107", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.107.tgz", + "integrity": "sha512-Eyzo2XRqWOxqhE1gk9h7LWmUf4Bp4Xn2Ttb0ayAXFp6YSTxQIThXcT9kipXZqcpxcmDwoq8iWbbf2P8XL743EA==", + "dev": true, + "optional": true + }, + "@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "dev": true + }, + "@swc/helpers": { + "version": "0.5.11", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.11.tgz", + "integrity": "sha512-YNlnKRWF2sVojTpIyzwou9XoTNbzbzONwRhOoniEioF1AtaitTvVZblaQRrAzChWQ1bLYyYSWzM18y4WwgzJ+A==", + "dev": true, + "requires": { + "tslib": "^2.4.0" + } + }, + "@swc/types": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.6.tgz", + "integrity": "sha512-/JLo/l2JsT/LRd80C3HfbmVpxOAJ11FO2RCEslFrgzLltoP9j8XIbsyDcfCt2WWyX+CM96rBoNM+IToAkFOugg==", + "dev": true, + "requires": { + "@swc/counter": "^0.1.3" + } + }, + "@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true + }, + "@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==" + }, + "@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "dev": true + }, + "@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "requires": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@types/babel__traverse": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.5.tgz", + "integrity": "sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==", + "dev": true, + "requires": { + "@babel/types": "^7.20.7" + } + }, + "@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "@types/bonjour": { + "version": "3.5.10", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", + "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", + "requires": { + "@types/node": "*" + } + }, + "@types/cacheable-request": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "requires": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" + } + }, + "@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "requires": { + "@types/node": "*" + } + }, + "@types/connect-history-api-fallback": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz", + "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==", + "requires": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, + "@types/eslint": { + "version": "8.4.10", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.10.tgz", + "integrity": "sha512-Sl/HOqN8NKPmhWo2VBEPm0nvHnu2LL3v9vKo8MEq0EtbJ4eVzGPl41VNPvn5E1i5poMk4/XD8UriLHpJvEP/Nw==", + "requires": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "@types/eslint-scope": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", + "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", + "requires": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" + }, + "@types/express": { + "version": "4.17.14", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.14.tgz", + "integrity": "sha512-TEbt+vaPFQ+xpxFLFssxUDXj5cWCxZJjIcB7Yg0k0GMHGtgtQgpvx/MUQUeAkNbA9AAGrwkAsoeItdTgS7FMyg==", + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.17.31", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.31.tgz", + "integrity": "sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q==", + "requires": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "@types/follow-redirects": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@types/follow-redirects/-/follow-redirects-1.14.1.tgz", + "integrity": "sha512-THBEFwqsLuU/K62B5JRwab9NW97cFmL4Iy34NTMX0bMycQVzq2q7PKOkhfivIwxdpa/J72RppgC42vCHfwKJ0Q==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/fs-extra": { + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.5.tgz", + "integrity": "sha512-0dzKcwO+S8s2kuF5Z9oUWatQJj5Uq/iqphEtE3GQJVRRYm/tD1LglU2UnXi2A8jLq5umkGouOXOR9y0n613ZwQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", + "dev": true, + "requires": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/hast": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.4.tgz", + "integrity": "sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==", + "requires": { + "@types/unist": "*" + } + }, + "@types/history": { + "version": "4.7.11", + "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.11.tgz", + "integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==" + }, + "@types/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==" + }, + "@types/http-cache-semantics": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", + "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==" + }, + "@types/http-proxy": { + "version": "1.17.9", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.9.tgz", + "integrity": "sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw==", + "requires": { + "@types/node": "*" + } + }, + "@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==" + }, + "@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "@types/jsdom": { + "version": "20.0.1", + "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz", + "integrity": "sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/tough-cookie": "*", + "parse5": "^7.0.0" + } + }, + "@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==" + }, + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, + "@types/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "requires": { + "@types/node": "*" + } + }, + "@types/linkify-it": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.2.tgz", + "integrity": "sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==" + }, + "@types/markdown-it": { + "version": "12.2.3", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", + "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", + "requires": { + "@types/linkify-it": "*", + "@types/mdurl": "*" + } + }, + "@types/mdast": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.11.tgz", + "integrity": "sha512-Y/uImid8aAwrEA24/1tcRZwpxX3pIFTSilcNDKSPn+Y2iDywSEachzRuvgAYYLR3wpGXAsMbv5lvKLDZLeYPAw==", + "requires": { + "@types/unist": "*" + } + }, + "@types/mdurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.2.tgz", + "integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==" + }, + "@types/mime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", + "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==" + }, + "@types/mime-types": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.1.tgz", + "integrity": "sha512-vXOTGVSLR2jMw440moWTC7H19iUyLtP3Z1YTj7cSsubOICinjMxFeb/V57v9QdyyPGbbWolUFSSmSiRSn94tFw==", + "dev": true + }, + "@types/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", + "dev": true + }, + "@types/node": { + "version": "18.19.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.11.tgz", + "integrity": "sha512-hzdHPKpDdp5bEcRq1XTlZ2ntVjLcHCTV73dEcGg02eSY/+9AZ+jlfz6i00+zOrunMWenjHuI49J8J7Y9uz50JQ==", + "requires": { + "undici-types": "~5.26.4" + } + }, + "@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true + }, + "@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" + }, + "@types/pg": { + "version": "8.6.5", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.6.5.tgz", + "integrity": "sha512-tOkGtAqRVkHa/PVZicq67zuujI4Oorfglsr2IbKofDwBSysnaqSx7W1mDqFqdkGE6Fbgh+PZAl0r/BWON/mozw==", + "dev": true, + "requires": { + "@types/node": "*", + "pg-protocol": "*", + "pg-types": "^2.2.0" + } + }, + "@types/prop-types": { + "version": "15.7.5", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" + }, + "@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" + }, + "@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" + }, + "@types/react": { + "version": "18.2.24", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.24.tgz", + "integrity": "sha512-Ee0Jt4sbJxMu1iDcetZEIKQr99J1Zfb6D4F3qfUWoR1JpInkY1Wdg4WwCyBjL257D0+jGqSl1twBjV8iCaC0Aw==", + "requires": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "@types/react-dom": { + "version": "18.2.9", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.9.tgz", + "integrity": "sha512-6nNhVzZ9joQ6F7lozrASuQKC0Kf6ArYMU+DqA2ZrUbB+d+9lC6ZLn1GxiEBI1edmAwvTULtuJ6uPZpv3XudwUg==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, + "@types/react-router": { + "version": "5.1.20", + "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.20.tgz", + "integrity": "sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==", + "requires": { + "@types/history": "^4.7.11", + "@types/react": "*" + } + }, + "@types/react-router-config": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/@types/react-router-config/-/react-router-config-5.0.7.tgz", + "integrity": "sha512-pFFVXUIydHlcJP6wJm7sDii5mD/bCmmAY0wQzq+M+uX7bqS95AQqHZWP1iNMKrWVQSuHIzj5qi9BvrtLX2/T4w==", + "requires": { + "@types/history": "^4.7.11", + "@types/react": "*", + "@types/react-router": "^5.1.0" + } + }, + "@types/react-router-dom": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.3.3.tgz", + "integrity": "sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==", + "requires": { + "@types/history": "^4.7.11", + "@types/react": "*", + "@types/react-router": "*" + } + }, + "@types/resolve": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", + "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/responselike": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", + "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", + "requires": { + "@types/node": "*" + } + }, + "@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" + }, + "@types/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-pSAff4IAxJjfAXUG6tFkO7dsSbTmf8CtUpfhhZ5VhkRpC4628tJhh3+V6H1E+/Gs9piSzYKT5yzHO5M4GG9jkw==", + "requires": { + "@types/node": "*" + } + }, + "@types/scheduler": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" + }, + "@types/semver": { + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", + "dev": true + }, + "@types/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==", + "requires": { + "@types/express": "*" + } + }, + "@types/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==", + "requires": { + "@types/mime": "*", + "@types/node": "*" + } + }, + "@types/sockjs": { + "version": "0.3.33", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", + "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==", + "requires": { + "@types/node": "*" + } + }, + "@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true + }, + "@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "dev": true + }, + "@types/unist": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", + "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==" + }, + "@types/vscode": { + "version": "1.73.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.73.0.tgz", + "integrity": "sha512-FhkfF7V3fj7S3WqXu7AxFesBLO3uMkdCPJJPbwyZXezv2xJ6xBWHYM2CmkkbO8wT9Fr3KipwxGGOoQRrYq7mHg==", + "dev": true + }, + "@types/ws": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", + "integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==", + "requires": { + "@types/node": "*" + } + }, + "@types/yargs": { + "version": "17.0.32", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", + "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==" + }, + "@typescript-eslint/eslint-plugin": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.20.0.tgz", + "integrity": "sha512-fTwGQUnjhoYHeSF6m5pWNkzmDDdsKELYrOBxhjMrofPqCkoC2k3B2wvGHFxa1CTIqkEn88nlW1HVMztjo2K8Hg==", + "dev": true, + "requires": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.20.0", + "@typescript-eslint/type-utils": "6.20.0", + "@typescript-eslint/utils": "6.20.0", + "@typescript-eslint/visitor-keys": "6.20.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "dependencies": { + "@typescript-eslint/scope-manager": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.20.0.tgz", + "integrity": "sha512-p4rvHQRDTI1tGGMDFQm+GtxP1ZHyAh64WANVoyEcNMpaTFn3ox/3CcgtIlELnRfKzSs/DwYlDccJEtr3O6qBvA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.20.0", + "@typescript-eslint/visitor-keys": "6.20.0" + } + }, + "@typescript-eslint/types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.20.0.tgz", + "integrity": "sha512-MM9mfZMAhiN4cOEcUOEx+0HmuaW3WBfukBZPCfwSqFnQy0grXYtngKCqpQN339X3RrwtzspWJrpbrupKYUSBXQ==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.20.0.tgz", + "integrity": "sha512-RnRya9q5m6YYSpBN7IzKu9FmLcYtErkDkc8/dKv81I9QiLLtVBHrjz+Ev/crAqgMNW2FCsoZF4g2QUylMnJz+g==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.20.0", + "@typescript-eslint/visitor-keys": "6.20.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + } + }, + "@typescript-eslint/utils": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.20.0.tgz", + "integrity": "sha512-/EKuw+kRu2vAqCoDwDCBtDRU6CTKbUmwwI7SH7AashZ+W+7o8eiyy6V2cdOqN49KsTcASWsC5QeghYuRDTyOOg==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.20.0", + "@typescript-eslint/types": "6.20.0", + "@typescript-eslint/typescript-estree": "6.20.0", + "semver": "^7.5.4" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.20.0.tgz", + "integrity": "sha512-E8Cp98kRe4gKHjJD4NExXKz/zOJ1A2hhZc+IMVD6i7w4yjIvh6VyuRI0gRtxAsXtoC35uGMaQ9rjI2zJaXDEAw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.20.0", + "eslint-visitor-keys": "^3.4.1" + } + }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, + "minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@typescript-eslint/experimental-utils": { + "version": "5.46.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.46.1.tgz", + "integrity": "sha512-M79mkB+wOuiBG8jzOVNA2h5izOip5CNPZV1K3tvE/qry/1Oh/bnKYhNWQNiH2h9O3B73YK60GmiqrUpprnQ5sQ==", + "dev": true, + "requires": { + "@typescript-eslint/utils": "5.46.1" + }, + "dependencies": { + "@typescript-eslint/scope-manager": { + "version": "5.46.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.46.1.tgz", + "integrity": "sha512-iOChVivo4jpwUdrJZyXSMrEIM/PvsbbDOX1y3UCKjSgWn+W89skxWaYXACQfxmIGhPVpRWK/VWPYc+bad6smIA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.46.1", + "@typescript-eslint/visitor-keys": "5.46.1" + } + }, + "@typescript-eslint/types": { + "version": "5.46.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.46.1.tgz", + "integrity": "sha512-Z5pvlCaZgU+93ryiYUwGwLl9AQVB/PQ1TsJ9NZ/gHzZjN7g9IAn6RSDkpCV8hqTwAiaj6fmCcKSQeBPlIpW28w==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "5.46.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.46.1.tgz", + "integrity": "sha512-j9W4t67QiNp90kh5Nbr1w92wzt+toiIsaVPnEblB2Ih2U9fqBTyqV9T3pYWZBRt6QoMh/zVWP59EpuCjc4VRBg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.46.1", + "@typescript-eslint/visitor-keys": "5.46.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/utils": { + "version": "5.46.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.46.1.tgz", + "integrity": "sha512-RBdBAGv3oEpFojaCYT4Ghn4775pdjvwfDOfQ2P6qzNVgQOVrnSPe5/Pb88kv7xzYQjoio0eKHKB9GJ16ieSxvA==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.46.1", + "@typescript-eslint/types": "5.46.1", + "@typescript-eslint/typescript-estree": "5.46.1", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0", + "semver": "^7.3.7" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.46.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.46.1.tgz", + "integrity": "sha512-jczZ9noovXwy59KjRTk1OftT78pwygdcmCuBf8yMoWt/8O8l+6x2LSEze0E4TeepXK4MezW3zGSyoDRZK7Y9cg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.46.1", + "eslint-visitor-keys": "^3.3.0" + } + }, + "fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@typescript-eslint/parser": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.20.0.tgz", + "integrity": "sha512-bYerPDF/H5v6V76MdMYhjwmwgMA+jlPVqjSDq2cRqMi8bP5sR3Z+RLOiOMad3nsnmDVmn2gAFCyNgh/dIrfP/w==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "6.20.0", + "@typescript-eslint/types": "6.20.0", + "@typescript-eslint/typescript-estree": "6.20.0", + "@typescript-eslint/visitor-keys": "6.20.0", + "debug": "^4.3.4" + }, + "dependencies": { + "@typescript-eslint/scope-manager": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.20.0.tgz", + "integrity": "sha512-p4rvHQRDTI1tGGMDFQm+GtxP1ZHyAh64WANVoyEcNMpaTFn3ox/3CcgtIlELnRfKzSs/DwYlDccJEtr3O6qBvA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.20.0", + "@typescript-eslint/visitor-keys": "6.20.0" + } + }, + "@typescript-eslint/types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.20.0.tgz", + "integrity": "sha512-MM9mfZMAhiN4cOEcUOEx+0HmuaW3WBfukBZPCfwSqFnQy0grXYtngKCqpQN339X3RrwtzspWJrpbrupKYUSBXQ==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.20.0.tgz", + "integrity": "sha512-RnRya9q5m6YYSpBN7IzKu9FmLcYtErkDkc8/dKv81I9QiLLtVBHrjz+Ev/crAqgMNW2FCsoZF4g2QUylMnJz+g==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.20.0", + "@typescript-eslint/visitor-keys": "6.20.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.20.0.tgz", + "integrity": "sha512-E8Cp98kRe4gKHjJD4NExXKz/zOJ1A2hhZc+IMVD6i7w4yjIvh6VyuRI0gRtxAsXtoC35uGMaQ9rjI2zJaXDEAw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.20.0", + "eslint-visitor-keys": "^3.4.1" + } + }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, + "minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@typescript-eslint/scope-manager": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" + } + }, + "@typescript-eslint/type-utils": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.20.0.tgz", + "integrity": "sha512-qnSobiJQb1F5JjN0YDRPHruQTrX7ICsmltXhkV536mp4idGAYrIyr47zF/JmkJtEcAVnIz4gUYJ7gOZa6SmN4g==", + "dev": true, + "requires": { + "@typescript-eslint/typescript-estree": "6.20.0", + "@typescript-eslint/utils": "6.20.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "dependencies": { + "@typescript-eslint/scope-manager": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.20.0.tgz", + "integrity": "sha512-p4rvHQRDTI1tGGMDFQm+GtxP1ZHyAh64WANVoyEcNMpaTFn3ox/3CcgtIlELnRfKzSs/DwYlDccJEtr3O6qBvA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.20.0", + "@typescript-eslint/visitor-keys": "6.20.0" + } + }, + "@typescript-eslint/types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.20.0.tgz", + "integrity": "sha512-MM9mfZMAhiN4cOEcUOEx+0HmuaW3WBfukBZPCfwSqFnQy0grXYtngKCqpQN339X3RrwtzspWJrpbrupKYUSBXQ==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.20.0.tgz", + "integrity": "sha512-RnRya9q5m6YYSpBN7IzKu9FmLcYtErkDkc8/dKv81I9QiLLtVBHrjz+Ev/crAqgMNW2FCsoZF4g2QUylMnJz+g==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.20.0", + "@typescript-eslint/visitor-keys": "6.20.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + } + }, + "@typescript-eslint/utils": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.20.0.tgz", + "integrity": "sha512-/EKuw+kRu2vAqCoDwDCBtDRU6CTKbUmwwI7SH7AashZ+W+7o8eiyy6V2cdOqN49KsTcASWsC5QeghYuRDTyOOg==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.20.0", + "@typescript-eslint/types": "6.20.0", + "@typescript-eslint/typescript-estree": "6.20.0", + "semver": "^7.5.4" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.20.0.tgz", + "integrity": "sha512-E8Cp98kRe4gKHjJD4NExXKz/zOJ1A2hhZc+IMVD6i7w4yjIvh6VyuRI0gRtxAsXtoC35uGMaQ9rjI2zJaXDEAw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.20.0", + "eslint-visitor-keys": "^3.4.1" + } + }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, + "minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@typescript-eslint/types": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "dependencies": { + "fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@typescript-eslint/utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", + "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" + }, + "dependencies": { + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" + } + }, + "@vitest/coverage-v8": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-1.5.3.tgz", + "integrity": "sha512-DPyGSu/fPHOJuPxzFSQoT4N/Fu/2aJfZRtEpEp8GI7NHsXBGE94CQ+pbEGBUMFjatsHPDJw/+TAF9r4ens2CNw==", + "dev": true, + "requires": { + "@ampproject/remapping": "^2.2.1", + "@bcoe/v8-coverage": "^0.2.3", + "debug": "^4.3.4", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-report": "^3.0.1", + "istanbul-lib-source-maps": "^5.0.4", + "istanbul-reports": "^3.1.6", + "magic-string": "^0.30.5", + "magicast": "^0.3.3", + "picocolors": "^1.0.0", + "std-env": "^3.5.0", + "strip-literal": "^2.0.0", + "test-exclude": "^6.0.0" + }, + "dependencies": { + "istanbul-lib-source-maps": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.4.tgz", + "integrity": "sha512-wHOoEsNJTVltaJp8eVkm8w+GVkVNHT2YDYo53YdzQEL2gWm1hBX5cGFR9hQJtuGLebidVX7et3+dmDZrmclduw==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.23", + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0" + } + }, + "magic-string": { + "version": "0.30.10", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz", + "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==", + "dev": true, + "requires": { + "@jridgewell/sourcemap-codec": "^1.4.15" + } + } + } + }, + "@vitest/expect": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.5.3.tgz", + "integrity": "sha512-y+waPz31pOFr3rD7vWTbwiLe5+MgsMm40jTZbQE8p8/qXyBX3CQsIXRx9XK12IbY7q/t5a5aM/ckt33b4PxK2g==", + "dev": true, + "requires": { + "@vitest/spy": "1.5.3", + "@vitest/utils": "1.5.3", + "chai": "^4.3.10" + } + }, + "@vitest/runner": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.5.3.tgz", + "integrity": "sha512-7PlfuReN8692IKQIdCxwir1AOaP5THfNkp0Uc4BKr2na+9lALNit7ub9l3/R7MP8aV61+mHKRGiqEKRIwu6iiQ==", + "dev": true, + "requires": { + "@vitest/utils": "1.5.3", + "p-limit": "^5.0.0", + "pathe": "^1.1.1" + }, + "dependencies": { + "p-limit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-5.0.0.tgz", + "integrity": "sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==", + "dev": true, + "requires": { + "yocto-queue": "^1.0.0" + } + }, + "yocto-queue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", + "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "dev": true + } + } + }, + "@vitest/snapshot": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.5.3.tgz", + "integrity": "sha512-K3mvIsjyKYBhNIDujMD2gfQEzddLe51nNOAf45yKRt/QFJcUIeTQd2trRvv6M6oCBHNVnZwFWbQ4yj96ibiDsA==", + "dev": true, + "requires": { + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "pretty-format": "^29.7.0" + }, + "dependencies": { + "magic-string": { + "version": "0.30.10", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz", + "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==", + "dev": true, + "requires": { + "@jridgewell/sourcemap-codec": "^1.4.15" + } + } + } + }, + "@vitest/spy": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.5.3.tgz", + "integrity": "sha512-Llj7Jgs6lbnL55WoshJUUacdJfjU2honvGcAJBxhra5TPEzTJH8ZuhI3p/JwqqfnTr4PmP7nDmOXP53MS7GJlg==", + "dev": true, + "requires": { + "tinyspy": "^2.2.0" + } + }, + "@vitest/ui": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-1.5.3.tgz", + "integrity": "sha512-DoSA5YxcUmeBEK7kJHzXiL2I0d9AijWI33arnUrwiWFDxgZPDxTjvSVsiXhe8qfqhloIHkwazl5E2rhlDd/ErA==", + "dev": true, + "requires": { + "@vitest/utils": "1.5.3", + "fast-glob": "^3.3.2", + "fflate": "^0.8.1", + "flatted": "^3.2.9", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "sirv": "^2.0.4" + }, + "dependencies": { + "fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "mrmime": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz", + "integrity": "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==", + "dev": true + }, + "sirv": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz", + "integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==", + "dev": true, + "requires": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + } + }, + "totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true + } + } + }, + "@vitest/utils": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.5.3.tgz", + "integrity": "sha512-rE9DTN1BRhzkzqNQO+kw8ZgfeEBCLXiHJwetk668shmNBpSagQxneT5eSqEBLP+cqSiAeecvQmbpFfdMyLcIQA==", + "dev": true, + "requires": { + "diff-sequences": "^29.6.3", + "estree-walker": "^3.0.3", + "loupe": "^2.3.7", + "pretty-format": "^29.7.0" + }, + "dependencies": { + "estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "requires": { + "@types/estree": "^1.0.0" + } + } + } + }, + "@vscode/vsce": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-2.19.0.tgz", + "integrity": "sha512-dAlILxC5ggOutcvJY24jxz913wimGiUrHaPkk16Gm9/PGFbz1YezWtrXsTKUtJws4fIlpX2UIlVlVESWq8lkfQ==", + "dev": true, + "requires": { + "azure-devops-node-api": "^11.0.1", + "chalk": "^2.4.2", + "cheerio": "^1.0.0-rc.9", + "commander": "^6.1.0", + "glob": "^7.0.6", + "hosted-git-info": "^4.0.2", + "jsonc-parser": "^3.2.0", + "keytar": "^7.7.0", + "leven": "^3.1.0", + "markdown-it": "^12.3.2", + "mime": "^1.3.4", + "minimatch": "^3.0.3", + "parse-semver": "^1.1.1", + "read": "^1.0.7", + "semver": "^5.1.0", + "tmp": "^0.2.1", + "typed-rest-client": "^1.8.4", + "url-join": "^4.0.1", + "xml2js": "^0.5.0", + "yauzl": "^2.3.1", + "yazl": "^2.2.2" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@webassemblyjs/ast": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", + "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", + "requires": { + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==" + }, + "@webassemblyjs/helper-api-error": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==" + }, + "@webassemblyjs/helper-buffer": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", + "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==" + }, + "@webassemblyjs/helper-numbers": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", + "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", + "requires": { + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==" + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", + "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", + "requires": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", + "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", + "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==" + }, + "@webassemblyjs/wasm-edit": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", + "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", + "requires": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-opt": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6", + "@webassemblyjs/wast-printer": "1.11.6" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", + "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", + "requires": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", + "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", + "requires": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", + "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", + "requires": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", + "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", + "requires": { + "@webassemblyjs/ast": "1.11.6", + "@xtuc/long": "4.2.2" + } + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" + }, + "@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", + "dev": true + }, + "@yarnpkg/parsers": { + "version": "3.0.0-rc.46", + "resolved": "https://registry.npmjs.org/@yarnpkg/parsers/-/parsers-3.0.0-rc.46.tgz", + "integrity": "sha512-aiATs7pSutzda/rq8fnuPwTglyVwjM22bNnK2ZgjrpAjQHSSl3lztd2f9evst1W/qnC58DRz7T7QndUDumAR4Q==", + "dev": true, + "requires": { + "js-yaml": "^3.10.0", + "tslib": "^2.4.0" + } + }, + "@zkochan/js-yaml": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@zkochan/js-yaml/-/js-yaml-0.0.6.tgz", + "integrity": "sha512-nzvgl3VfhcELQ8LyVrYOru+UtAy1nrygk2+AGbTm8a5YcO6o8lSjAT+pfg3vJWxIoZKOUhrK6UU7xW/+00kQrg==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + }, + "dependencies": { + "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 + } + } + }, + "abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "dev": true + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "requires": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + } + }, + "acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==" + }, + "acorn-globals": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", + "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", + "dev": true, + "requires": { + "acorn": "^8.1.0", + "acorn-walk": "^8.0.2" + } + }, + "acorn-import-assertions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", + "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==" + }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==" + }, + "acorn-walk": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", + "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==" + }, + "address": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/address/-/address-1.2.2.tgz", + "integrity": "sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==" + }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "requires": { + "debug": "4" + } + }, + "agentkeepalive": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.2.1.tgz", + "integrity": "sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA==", + "optional": true, + "requires": { + "debug": "^4.1.0", + "depd": "^1.1.2", + "humanize-ms": "^1.2.1" + }, + "dependencies": { + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "optional": true + } + } + }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "requires": { + "ajv": "^8.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz", + "integrity": "sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + } + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==" + }, + "algoliasearch": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.17.2.tgz", + "integrity": "sha512-VFu43JJNYIW74awp7oeQcQsPcxOhd8psqBDTfyNO2Zt6L1NqnNMTVnaIdQ+8dtKqUDBqQZp0szPxECvX8CK2Fg==", + "requires": { + "@algolia/cache-browser-local-storage": "4.17.2", + "@algolia/cache-common": "4.17.2", + "@algolia/cache-in-memory": "4.17.2", + "@algolia/client-account": "4.17.2", + "@algolia/client-analytics": "4.17.2", + "@algolia/client-common": "4.17.2", + "@algolia/client-personalization": "4.17.2", + "@algolia/client-search": "4.17.2", + "@algolia/logger-common": "4.17.2", + "@algolia/logger-console": "4.17.2", + "@algolia/requester-browser-xhr": "4.17.2", + "@algolia/requester-common": "4.17.2", + "@algolia/requester-node-http": "4.17.2", + "@algolia/transporter": "4.17.2" + } + }, + "algoliasearch-helper": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.13.1.tgz", + "integrity": "sha512-4ZUVmtMfe4+RgI1v53eGW4YQUsPJrZbi4AecGgmotkPXp0ysW4b2zG+/VpxgOz8cwwwkVLeEskL+iECzCwdpFw==", + "requires": { + "@algolia/events": "^4.0.1" + } + }, + "ansi-align": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", + "requires": { + "string-width": "^4.1.0" + } + }, + "ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true + }, + "ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "requires": { + "type-fest": "^0.21.3" + } + }, + "ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==" + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" + }, + "archiver": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz", + "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==", + "requires": { + "archiver-utils": "^2.1.0", + "async": "^3.2.4", + "buffer-crc32": "^0.2.1", + "readable-stream": "^3.6.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^2.2.0", + "zip-stream": "^4.1.0" + } + }, + "archiver-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", + "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", + "requires": { + "glob": "^7.1.4", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^2.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "requires": { + "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" + } + }, + "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==" + }, + "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==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + } + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==" + }, + "array-includes": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", + "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "get-intrinsic": "^1.1.3", + "is-string": "^1.0.7" + } + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==" + }, + "array.prototype.flat": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", + "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0" + } + }, + "array.prototype.flatmap": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz", + "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0" + } + }, + "array.prototype.tosorted": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.1.tgz", + "integrity": "sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0", + "get-intrinsic": "^1.1.3" + } + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" + }, + "assert": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-2.0.0.tgz", + "integrity": "sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==", + "requires": { + "es6-object-assign": "^1.1.0", + "is-nan": "^1.2.1", + "object-is": "^1.0.1", + "util": "^0.12.0" + } + }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true + }, + "ast-types-flow": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", + "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==", + "dev": true + }, + "async": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, + "at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" + }, + "autoprefixer": { + "version": "10.4.13", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz", + "integrity": "sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg==", + "requires": { + "browserslist": "^4.21.4", + "caniuse-lite": "^1.0.30001426", + "fraction.js": "^4.2.0", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + } + }, + "available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==" + }, + "axe-core": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.6.0.tgz", + "integrity": "sha512-L3ZNbXPTxMrl0+qTXAzn9FBRvk5XdO56K8CvcCKtlxv44Aw2w2NCclGuvCWxHPw1Riiq3ncP/sxFYj2nUqdoTw==", + "dev": true + }, + "axios": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz", + "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==", + "dev": true, + "requires": { + "follow-redirects": "^1.15.4", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "axobject-query": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", + "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==", + "dev": true + }, + "azure-devops-node-api": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-11.2.0.tgz", + "integrity": "sha512-XdiGPhrpaT5J8wdERRKs5g8E0Zy1pvOYTli7z9E8nmOn3YGp4FhtjhrOyFmX/8veWCwdI69mCHKJw6l+4J/bHA==", + "dev": true, + "requires": { + "tunnel": "0.0.6", + "typed-rest-client": "^1.8.4" + } + }, + "babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "requires": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + } + }, + "babel-loader": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.3.0.tgz", + "integrity": "sha512-H8SvsMF+m9t15HNLMipppzkC+Y2Yq+v3SonZyU70RBL/h1gxPkH08Ot8pEE9Z4Kd+czyWJClmFS8qzIP9OZ04Q==", + "requires": { + "find-cache-dir": "^3.3.1", + "loader-utils": "^2.0.0", + "make-dir": "^3.1.0", + "schema-utils": "^2.6.5" + } + }, + "babel-plugin-apply-mdx-type-prop": { + "version": "1.6.22", + "resolved": "https://registry.npmjs.org/babel-plugin-apply-mdx-type-prop/-/babel-plugin-apply-mdx-type-prop-1.6.22.tgz", + "integrity": "sha512-VefL+8o+F/DfK24lPZMtJctrCVOfgbqLAGZSkxwhazQv4VxPg3Za/i40fu22KR2m8eEda+IfSOlPLUSIiLcnCQ==", + "requires": { + "@babel/helper-plugin-utils": "7.10.4", + "@mdx-js/util": "1.6.22" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", + "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==" + } + } + }, + "babel-plugin-const-enum": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-const-enum/-/babel-plugin-const-enum-1.2.0.tgz", + "integrity": "sha512-o1m/6iyyFnp9MRsK1dHF3bneqyf3AlM2q3A/YbgQr2pCat6B6XJVDv2TXqzfY2RYUi4mak6WAksSBPlyYGx9dg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-syntax-typescript": "^7.3.3", + "@babel/traverse": "^7.16.0" + } + }, + "babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "requires": { + "object.assign": "^4.1.0" + } + }, + "babel-plugin-extract-import-names": { + "version": "1.6.22", + "resolved": "https://registry.npmjs.org/babel-plugin-extract-import-names/-/babel-plugin-extract-import-names-1.6.22.tgz", + "integrity": "sha512-yJ9BsJaISua7d8zNT7oRG1ZLBJCIdZ4PZqmH8qa9N5AK01ifk3fnkc98AXhtzE7UkfCsEumvoQWgoYLhOnJ7jQ==", + "requires": { + "@babel/helper-plugin-utils": "7.10.4" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", + "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==" + } + } + }, + "babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "dependencies": { + "istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "requires": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + } + }, + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true + } + } + }, + "babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "requires": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + } + }, + "babel-plugin-macros": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz", + "integrity": "sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.7.2", + "cosmiconfig": "^6.0.0", + "resolve": "^1.12.0" + }, + "dependencies": { + "cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "dev": true, + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" + } + } + } + }, + "babel-plugin-polyfill-corejs2": { + "version": "0.4.8", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.8.tgz", + "integrity": "sha512-OtIuQfafSzpo/LhnJaykc0R/MMnuLSSVjVYy9mHArIZ9qTCSZ6TpWCuEKZYVoN//t8HqBNScHrOtCrIK5IaGLg==", + "requires": { + "@babel/compat-data": "^7.22.6", + "@babel/helper-define-polyfill-provider": "^0.5.0", + "semver": "^6.3.1" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + } + } + }, + "babel-plugin-polyfill-corejs3": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.9.0.tgz", + "integrity": "sha512-7nZPG1uzK2Ymhy/NbaOWTg3uibM2BmGASS4vHS4szRZAIR8R6GwA/xAujpdrXU5iyklrimWnLWU+BLF9suPTqg==", + "requires": { + "@babel/helper-define-polyfill-provider": "^0.5.0", + "core-js-compat": "^3.34.0" + } + }, + "babel-plugin-polyfill-regenerator": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.5.tgz", + "integrity": "sha512-OJGYZlhLqBh2DDHeqAxWB1XIvr49CxiJ2gIt61/PU55CQK4Z58OzMqjDe1zwQdQk+rBYsRc+1rJmdajM3gimHg==", + "requires": { + "@babel/helper-define-polyfill-provider": "^0.5.0" + } + }, + "babel-plugin-transform-async-to-promises": { + "version": "0.8.18", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-to-promises/-/babel-plugin-transform-async-to-promises-0.8.18.tgz", + "integrity": "sha512-WpOrF76nUHijnNn10eBGOHZmXQC8JYRME9rOLxStOga7Av2VO53ehVFvVNImMksVtQuL2/7ZNxEgxnx7oo/3Hw==", + "dev": true + }, + "babel-plugin-transform-react-remove-prop-types": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz", + "integrity": "sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA==", + "dev": true + }, + "babel-plugin-transform-typescript-metadata": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-typescript-metadata/-/babel-plugin-transform-typescript-metadata-0.3.2.tgz", + "integrity": "sha512-mWEvCQTgXQf48yDqgN7CH50waTyYBeP2Lpqx4nNWab9sxEpdXVeKgfj1qYI2/TgUPQtNFZ85i3PemRtnXVYYJg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dev": true, + "requires": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + } + }, + "babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "requires": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + } + }, + "babel-preset-react-app": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-react-app/-/babel-preset-react-app-10.0.1.tgz", + "integrity": "sha512-b0D9IZ1WhhCWkrTXyFuIIgqGzSkRIH5D5AmB0bXbzYAB1OBAwHcUeyWW2LorutLWF5btNo/N7r/cIdmvvKJlYg==", + "dev": true, + "requires": { + "@babel/core": "^7.16.0", + "@babel/plugin-proposal-class-properties": "^7.16.0", + "@babel/plugin-proposal-decorators": "^7.16.4", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.0", + "@babel/plugin-proposal-numeric-separator": "^7.16.0", + "@babel/plugin-proposal-optional-chaining": "^7.16.0", + "@babel/plugin-proposal-private-methods": "^7.16.0", + "@babel/plugin-transform-flow-strip-types": "^7.16.0", + "@babel/plugin-transform-react-display-name": "^7.16.0", + "@babel/plugin-transform-runtime": "^7.16.4", + "@babel/preset-env": "^7.16.4", + "@babel/preset-react": "^7.16.0", + "@babel/preset-typescript": "^7.16.0", + "@babel/runtime": "^7.16.3", + "babel-plugin-macros": "^3.1.0", + "babel-plugin-transform-react-remove-prop-types": "^0.4.24" + }, + "dependencies": { + "babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + } + } + } + }, + "bail": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz", + "integrity": "sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==" + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "base16": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/base16/-/base16-1.0.0.tgz", + "integrity": "sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ==" + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + }, + "dependencies": { + "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 + } + } + }, + "batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==" + }, + "big-integer": { + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==" + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==" + }, + "binary": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", + "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==", + "requires": { + "buffers": "~0.1.1", + "chainsaw": "~0.1.0" + } + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" + }, + "bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "requires": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "dependencies": { + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + } + } + }, + "bonjour-service": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.0.14.tgz", + "integrity": "sha512-HIMbgLnk1Vqvs6B4Wq5ep7mxvj9sGz5d1JJyDNSGNIdA/w2MCz6GTjWTdjqOJV1bEPj+6IkxDvWNFKEBxNt4kQ==", + "requires": { + "array-flatten": "^2.1.2", + "dns-equal": "^1.0.0", + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" + } + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" + }, + "boxen": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-6.2.1.tgz", + "integrity": "sha512-H4PEsJXfFI/Pt8sjDWbHlQPx4zL/bvSQjcilJmaulGt5mLDorHOHpmdXAJcBcmru7PhYSp/cDMWRko4ZUMFkSw==", + "requires": { + "ansi-align": "^3.0.1", + "camelcase": "^6.2.0", + "chalk": "^4.1.2", + "cli-boxes": "^3.0.0", + "string-width": "^5.0.1", + "type-fest": "^2.5.0", + "widest-line": "^4.0.1", + "wrap-ansi": "^8.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==" + }, + "ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==" + }, + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==" + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + } + }, + "strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "requires": { + "ansi-regex": "^6.0.1" + } + }, + "type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==" + }, + "wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "requires": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + } + } + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "requires": { + "fill-range": "^7.0.1" + } + }, + "browserslist": { + "version": "4.22.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.3.tgz", + "integrity": "sha512-UAp55yfwNv0klWNapjs/ktHoguxuQNGnOzxYmfnXIS+8AsRDZkSDxg7R1AX3GKzn078SBI5dzwzj/Yx0Or0e3A==", + "requires": { + "caniuse-lite": "^1.0.30001580", + "electron-to-chromium": "^1.4.648", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" + } + }, + "bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "requires": { + "node-int64": "^0.4.0" + } + }, + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==" + }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "buffer-indexof-polyfill": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", + "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==" + }, + "buffer-writer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", + "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==" + }, + "buffers": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", + "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==" + }, + "builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "dev": true + }, + "builtins": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", + "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", + "dev": true, + "requires": { + "semver": "^7.0.0" + } + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==" + }, + "cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true + }, + "cacache": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", + "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "optional": true, + "requires": { + "@npmcli/fs": "^1.0.0", + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + } + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" + }, + "camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "requires": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==" + }, + "caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "requires": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "caniuse-lite": { + "version": "1.0.30001581", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001581.tgz", + "integrity": "sha512-whlTkwhqV2tUmP3oYhtNfaWGYHDdS3JYFQBKXxcUR9qqPWsRhFHhoISO2Xnl/g0xyKzht9mI1LZpiNWfMzHixQ==" + }, + "catharsis": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", + "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", + "requires": { + "lodash": "^4.17.15" + } + }, + "ccount": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-1.1.0.tgz", + "integrity": "sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg==" + }, + "chai": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.4.1.tgz", + "integrity": "sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==", + "dev": true, + "requires": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.0.8" + } + }, + "chainsaw": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", + "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==", + "requires": { + "traverse": ">=0.3.0 <0.4" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true + }, + "character-entities": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", + "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==" + }, + "character-entities-legacy": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", + "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==" + }, + "character-reference-invalid": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", + "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==" + }, + "check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "requires": { + "get-func-name": "^2.0.2" + } + }, + "cheerio": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", + "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", + "requires": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "htmlparser2": "^8.0.1", + "parse5": "^7.0.0", + "parse5-htmlparser2-tree-adapter": "^7.0.0" + }, + "dependencies": { + "dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "requires": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + } + }, + "domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "requires": { + "domelementtype": "^2.3.0" + } + }, + "domutils": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz", + "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==", + "requires": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.1" + } + }, + "entities": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", + "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==" + } + } + }, + "cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "requires": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "dependencies": { + "css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "requires": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + } + }, + "dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "requires": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + } + }, + "domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "requires": { + "domelementtype": "^2.3.0" + } + }, + "domutils": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz", + "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==", + "requires": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.1" + } + }, + "entities": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", + "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==" + } + } + }, + "chevrotain": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-11.0.3.tgz", + "integrity": "sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw==", + "requires": { + "@chevrotain/cst-dts-gen": "11.0.3", + "@chevrotain/gast": "11.0.3", + "@chevrotain/regexp-to-ast": "11.0.3", + "@chevrotain/types": "11.0.3", + "@chevrotain/utils": "11.0.3", + "lodash-es": "4.17.21" + } + }, + "chevrotain-allstar": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/chevrotain-allstar/-/chevrotain-allstar-0.3.1.tgz", + "integrity": "sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw==", + "requires": { + "lodash-es": "^4.17.21" + } + }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.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" + } + }, + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" + }, + "chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==" + }, + "ci-info": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.5.0.tgz", + "integrity": "sha512-yH4RezKOGlOhxkmhbeNuC4eYZKAUsEaGtBuBzDDP1eFUKiccDWzBABxBfOx31IDwDIXMTxWuwAxUGModvkbuVw==" + }, + "cjs-module-lexer": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", + "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", + "dev": true + }, + "clean-css": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.2.tgz", + "integrity": "sha512-JVJbM+f3d3Q704rF4bqQ5UUyTtuJ0JRKNbTKVEeujCCBoMdkEi+V+e8oktO9qGQNSvHrFTM6JZRXrUvGR1czww==", + "requires": { + "source-map": "~0.6.0" + } + }, + "clean-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clean-regexp/-/clean-regexp-1.0.0.tgz", + "integrity": "sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==" + }, + "cli-boxes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", + "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==" + }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-spinners": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", + "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==", + "dev": true + }, + "cli-table3": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz", + "integrity": "sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==", + "requires": { + "@colors/colors": "1.5.0", + "string-width": "^4.2.0" + } + }, + "cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + } + }, + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true + }, + "clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "requires": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + } + }, + "clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "requires": { + "mimic-response": "^1.0.0" + }, + "dependencies": { + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" + } + } + }, + "clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==" + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true + }, + "collapse-white-space": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.6.tgz", + "integrity": "sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ==" + }, + "collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "dev": true + }, + "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==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==" + }, + "colord": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==" + }, + "colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==" + }, + "columnify": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/columnify/-/columnify-1.6.0.tgz", + "integrity": "sha512-lomjuFZKfM6MSAnV9aCZC9sc0qGbmZdfygNv+nCpqVkSKdCxCklLtd16O0EILGkImHw9ZpHkAnHaB+8Zxq5W6Q==", + "dev": true, + "requires": { + "strip-ansi": "^6.0.1", + "wcwidth": "^1.0.0" + } + }, + "combine-promises": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/combine-promises/-/combine-promises-1.1.0.tgz", + "integrity": "sha512-ZI9jvcLDxqwaXEixOhArm3r7ReIivsXkpbyEWyeOhzz1QS0iSgBPnWvEqvIQtYyamGCYA88gFhmUrs9hrrQ0pg==" + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "comma-separated-tokens": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz", + "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==" + }, + "commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==" + }, + "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 + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==" + }, + "compress-commons": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz", + "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==", + "requires": { + "buffer-crc32": "^0.2.13", + "crc32-stream": "^4.0.2", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + } + }, + "compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "requires": { + "mime-db": ">= 1.43.0 < 2" + } + }, + "compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "requires": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "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==" + } + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "concat-with-sourcemaps": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz", + "integrity": "sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg==", + "dev": true, + "requires": { + "source-map": "^0.6.1" + } + }, + "confbox": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.7.tgz", + "integrity": "sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==", + "dev": true + }, + "configstore": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", + "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", + "requires": { + "dot-prop": "^5.2.0", + "graceful-fs": "^4.1.2", + "make-dir": "^3.0.0", + "unique-string": "^2.0.0", + "write-file-atomic": "^3.0.0", + "xdg-basedir": "^4.0.0" + }, + "dependencies": { + "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==", + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + } + } + }, + "confusing-browser-globals": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", + "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", + "dev": true + }, + "connect-history-api-fallback": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==" + }, + "consola": { + "version": "2.15.3", + "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz", + "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==" + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" + }, + "content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "requires": { + "safe-buffer": "5.2.1" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "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==" + }, + "cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "copy-anything": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz", + "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", + "dev": true, + "requires": { + "is-what": "^3.14.1" + } + }, + "copy-text-to-clipboard": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/copy-text-to-clipboard/-/copy-text-to-clipboard-3.1.0.tgz", + "integrity": "sha512-PFM6BnjLnOON/lB3ta/Jg7Ywsv+l9kQGD4TWDCSlRBGmqnnTM5MrDkhAFgw+8HZt0wW6Q2BBE4cmy9sq+s9Qng==" + }, + "copy-webpack-plugin": { + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-10.2.4.tgz", + "integrity": "sha512-xFVltahqlsRcyyJqQbDY6EYTtyQZF9rf+JPjwHObLdPFMEISqkFkr7mFoVOC6BfYS/dNThyoQKvziugm+OnwBg==", + "dev": true, + "requires": { + "fast-glob": "^3.2.7", + "glob-parent": "^6.0.1", + "globby": "^12.0.2", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "array-union": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-3.0.1.tgz", + "integrity": "sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw==", + "dev": true + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + }, + "globby": { + "version": "12.2.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-12.2.0.tgz", + "integrity": "sha512-wiSuFQLZ+urS9x2gGPl1H5drc5twabmm4m2gTR27XDFyjUHJUNsS8o/2aKyIF6IoBaR630atdher0XJ5g6OMmA==", + "dev": true, + "requires": { + "array-union": "^3.0.1", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.7", + "ignore": "^5.1.9", + "merge2": "^1.4.1", + "slash": "^4.0.0" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + } + }, + "slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true + } + } + }, + "core-js": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.26.1.tgz", + "integrity": "sha512-21491RRQVzUn0GGM9Z1Jrpr6PNPxPi+Za8OM9q4tksTSnlbXXGKK1nXNg/QvwFYettXvSX6zWKCtHHfjN4puyA==" + }, + "core-js-compat": { + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.35.1.tgz", + "integrity": "sha512-sftHa5qUJY3rs9Zht1WEnmkvXputCyDBczPnr7QDgL8n3qrF3CMXY4VPSYtOLLiOUJcah2WNXREd48iOl6mQIw==", + "requires": { + "browserslist": "^4.22.2" + } + }, + "core-js-pure": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.26.1.tgz", + "integrity": "sha512-VVXcDpp/xJ21KdULRq/lXdLzQAtX7+37LzpyfFM973il0tWSsDEoyzG38G14AjTpK9VTfiNM9jnFauq/CpaWGQ==" + }, + "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==" + }, + "corser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", + "integrity": "sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==", + "dev": true + }, + "cose-base": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz", + "integrity": "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==", + "requires": { + "layout-base": "^1.0.0" + } + }, + "cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "requires": { + "@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" + } + }, + "crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==" + }, + "crc32-stream": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz", + "integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==", + "requires": { + "crc-32": "^1.2.0", + "readable-stream": "^3.4.0" + } + }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "cross-fetch": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.6.tgz", + "integrity": "sha512-riRvo06crlE8HiqOwIpQhxwdOk4fOeR7FVM/wXoxchFEqMNUjvbs3bfo4OTgMEMHzppd4DxFBDbyySj8Cv781g==", + "requires": { + "node-fetch": "^2.6.11" + } + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==" + }, + "css-declaration-sorter": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.3.1.tgz", + "integrity": "sha512-fBffmak0bPAnyqc/HO8C3n2sHrp9wcqQz6ES9koRF2/mLOVAx9zIQ3Y7R29sYCteTPqMCwns4WYQoCX91Xl3+w==" + }, + "css-loader": { + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.2.tgz", + "integrity": "sha512-oqGbbVcBJkm8QwmnNzrFrWTnudnRZC+1eXikLJl0n4ljcfotgRifpg2a1lKy8jTrc4/d9A/ap1GFq1jDKG7J+Q==", + "requires": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.18", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.3.8" + }, + "dependencies": { + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "css-minimizer-webpack-plugin": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-5.0.1.tgz", + "integrity": "sha512-3caImjKFQkS+ws1TGcFn0V1HyDJFq1Euy589JlD6/3rV2kj+w7r5G9WDMgSHvpvXHNZ2calVypZWuEDQd9wfLg==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.18", + "cssnano": "^6.0.1", + "jest-worker": "^29.4.3", + "postcss": "^8.4.24", + "schema-utils": "^4.0.1", + "serialize-javascript": "^6.0.1" + }, + "dependencies": { + "ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true + }, + "css-declaration-sorter": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.1.1.tgz", + "integrity": "sha512-dZ3bVTEEc1vxr3Bek9vGwfB5Z6ESPULhcRvO472mfjVnj8jRcTnKO8/JTczlvxM10Myb+wBM++1MtdO76eWcaQ==", + "dev": true + }, + "css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "dev": true, + "requires": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + } + }, + "css-tree": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "dev": true, + "requires": { + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" + } + }, + "cssnano": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-6.0.3.tgz", + "integrity": "sha512-MRq4CIj8pnyZpcI2qs6wswoYoDD1t0aL28n+41c1Ukcpm56m1h6mCexIHBGjfZfnTqtGSSCP4/fB1ovxgjBOiw==", + "dev": true, + "requires": { + "cssnano-preset-default": "^6.0.3", + "lilconfig": "^3.0.0" + } + }, + "cssnano-preset-default": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-6.0.3.tgz", + "integrity": "sha512-4y3H370aZCkT9Ev8P4SO4bZbt+AExeKhh8wTbms/X7OLDo5E7AYUUy6YPxa/uF5Grf+AJwNcCnxKhZynJ6luBA==", + "dev": true, + "requires": { + "css-declaration-sorter": "^7.1.1", + "cssnano-utils": "^4.0.1", + "postcss-calc": "^9.0.1", + "postcss-colormin": "^6.0.2", + "postcss-convert-values": "^6.0.2", + "postcss-discard-comments": "^6.0.1", + "postcss-discard-duplicates": "^6.0.1", + "postcss-discard-empty": "^6.0.1", + "postcss-discard-overridden": "^6.0.1", + "postcss-merge-longhand": "^6.0.2", + "postcss-merge-rules": "^6.0.3", + "postcss-minify-font-values": "^6.0.1", + "postcss-minify-gradients": "^6.0.1", + "postcss-minify-params": "^6.0.2", + "postcss-minify-selectors": "^6.0.2", + "postcss-normalize-charset": "^6.0.1", + "postcss-normalize-display-values": "^6.0.1", + "postcss-normalize-positions": "^6.0.1", + "postcss-normalize-repeat-style": "^6.0.1", + "postcss-normalize-string": "^6.0.1", + "postcss-normalize-timing-functions": "^6.0.1", + "postcss-normalize-unicode": "^6.0.2", + "postcss-normalize-url": "^6.0.1", + "postcss-normalize-whitespace": "^6.0.1", + "postcss-ordered-values": "^6.0.1", + "postcss-reduce-initial": "^6.0.2", + "postcss-reduce-transforms": "^6.0.1", + "postcss-svgo": "^6.0.2", + "postcss-unique-selectors": "^6.0.2" + } + }, + "cssnano-utils": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-4.0.1.tgz", + "integrity": "sha512-6qQuYDqsGoiXssZ3zct6dcMxiqfT6epy7x4R0TQJadd4LWO3sPR6JH6ZByOvVLoZ6EdwPGgd7+DR1EmX3tiXQQ==", + "dev": true + }, + "csso": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", + "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", + "dev": true, + "requires": { + "css-tree": "~2.2.0" + }, + "dependencies": { + "css-tree": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", + "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", + "dev": true, + "requires": { + "mdn-data": "2.0.28", + "source-map-js": "^1.0.1" + } + }, + "mdn-data": { + "version": "2.0.28", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", + "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", + "dev": true + } + } + }, + "dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "requires": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + } + }, + "domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "requires": { + "domelementtype": "^2.3.0" + } + }, + "domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "dev": true, + "requires": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + } + }, + "entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "lilconfig": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.0.0.tgz", + "integrity": "sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g==", + "dev": true + }, + "mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", + "dev": true + }, + "postcss-calc": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-9.0.1.tgz", + "integrity": "sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.11", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-colormin": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-6.0.2.tgz", + "integrity": "sha512-TXKOxs9LWcdYo5cgmcSHPkyrLAh86hX1ijmyy6J8SbOhyv6ua053M3ZAM/0j44UsnQNIWdl8gb5L7xX2htKeLw==", + "dev": true, + "requires": { + "browserslist": "^4.22.2", + "caniuse-api": "^3.0.0", + "colord": "^2.9.1", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-convert-values": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-6.0.2.tgz", + "integrity": "sha512-aeBmaTnGQ+NUSVQT8aY0sKyAD/BaLJenEKZ03YK0JnDE1w1Rr8XShoxdal2V2H26xTJKr3v5haByOhJuyT4UYw==", + "dev": true, + "requires": { + "browserslist": "^4.22.2", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-discard-comments": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-6.0.1.tgz", + "integrity": "sha512-f1KYNPtqYLUeZGCHQPKzzFtsHaRuECe6jLakf/RjSRqvF5XHLZnM2+fXLhb8Qh/HBFHs3M4cSLb1k3B899RYIg==", + "dev": true + }, + "postcss-discard-duplicates": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-6.0.1.tgz", + "integrity": "sha512-1hvUs76HLYR8zkScbwyJ8oJEugfPV+WchpnA+26fpJ7Smzs51CzGBHC32RS03psuX/2l0l0UKh2StzNxOrKCYg==", + "dev": true + }, + "postcss-discard-empty": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-6.0.1.tgz", + "integrity": "sha512-yitcmKwmVWtNsrrRqGJ7/C0YRy53i0mjexBDQ9zYxDwTWVBgbU4+C9jIZLmQlTDT9zhml+u0OMFJh8+31krmOg==", + "dev": true + }, + "postcss-discard-overridden": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-6.0.1.tgz", + "integrity": "sha512-qs0ehZMMZpSESbRkw1+inkf51kak6OOzNRaoLd/U7Fatp0aN2HQ1rxGOrJvYcRAN9VpX8kUF13R2ofn8OlvFVA==", + "dev": true + }, + "postcss-merge-longhand": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-6.0.2.tgz", + "integrity": "sha512-+yfVB7gEM8SrCo9w2lCApKIEzrTKl5yS1F4yGhV3kSim6JzbfLGJyhR1B6X+6vOT0U33Mgx7iv4X9MVWuaSAfw==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0", + "stylehacks": "^6.0.2" + } + }, + "postcss-merge-rules": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-6.0.3.tgz", + "integrity": "sha512-yfkDqSHGohy8sGYIJwBmIGDv4K4/WrJPX355XrxQb/CSsT4Kc/RxDi6akqn5s9bap85AWgv21ArcUWwWdGNSHA==", + "dev": true, + "requires": { + "browserslist": "^4.22.2", + "caniuse-api": "^3.0.0", + "cssnano-utils": "^4.0.1", + "postcss-selector-parser": "^6.0.15" + } + }, + "postcss-minify-font-values": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-6.0.1.tgz", + "integrity": "sha512-tIwmF1zUPoN6xOtA/2FgVk1ZKrLcCvE0dpZLtzyyte0j9zUeB8RTbCqrHZGjJlxOvNWKMYtunLrrl7HPOiR46w==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-minify-gradients": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-6.0.1.tgz", + "integrity": "sha512-M1RJWVjd6IOLPl1hYiOd5HQHgpp6cvJVLrieQYS9y07Yo8itAr6jaekzJphaJFR0tcg4kRewCk3kna9uHBxn/w==", + "dev": true, + "requires": { + "colord": "^2.9.1", + "cssnano-utils": "^4.0.1", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-minify-params": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-6.0.2.tgz", + "integrity": "sha512-zwQtbrPEBDj+ApELZ6QylLf2/c5zmASoOuA4DzolyVGdV38iR2I5QRMsZcHkcdkZzxpN8RS4cN7LPskOkTwTZw==", + "dev": true, + "requires": { + "browserslist": "^4.22.2", + "cssnano-utils": "^4.0.1", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-minify-selectors": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-6.0.2.tgz", + "integrity": "sha512-0b+m+w7OAvZejPQdN2GjsXLv5o0jqYHX3aoV0e7RBKPCsB7TYG5KKWBFhGnB/iP3213Ts8c5H4wLPLMm7z28Sg==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.15" + } + }, + "postcss-normalize-charset": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-6.0.1.tgz", + "integrity": "sha512-aW5LbMNRZ+oDV57PF9K+WI1Z8MPnF+A8qbajg/T8PP126YrGX1f9IQx21GI2OlGz7XFJi/fNi0GTbY948XJtXg==", + "dev": true + }, + "postcss-normalize-display-values": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-6.0.1.tgz", + "integrity": "sha512-mc3vxp2bEuCb4LgCcmG1y6lKJu1Co8T+rKHrcbShJwUmKJiEl761qb/QQCfFwlrvSeET3jksolCR/RZuMURudw==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-positions": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-6.0.1.tgz", + "integrity": "sha512-HRsq8u/0unKNvm0cvwxcOUEcakFXqZ41fv3FOdPn916XFUrympjr+03oaLkuZENz3HE9RrQE9yU0Xv43ThWjQg==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-repeat-style": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-6.0.1.tgz", + "integrity": "sha512-Gbb2nmCy6tTiA7Sh2MBs3fj9W8swonk6lw+dFFeQT68B0Pzwp1kvisJQkdV6rbbMSd9brMlS8I8ts52tAGWmGQ==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-string": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-6.0.1.tgz", + "integrity": "sha512-5Fhx/+xzALJD9EI26Aq23hXwmv97Zfy2VFrt5PLT8lAhnBIZvmaT5pQk+NuJ/GWj/QWaKSKbnoKDGLbV6qnhXg==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-timing-functions": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-6.0.1.tgz", + "integrity": "sha512-4zcczzHqmCU7L5dqTB9rzeqPWRMc0K2HoR+Bfl+FSMbqGBUcP5LRfgcH4BdRtLuzVQK1/FHdFoGT3F7rkEnY+g==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-unicode": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-6.0.2.tgz", + "integrity": "sha512-Ff2VdAYCTGyMUwpevTZPZ4w0+mPjbZzLLyoLh/RMpqUqeQKZ+xMm31hkxBavDcGKcxm6ACzGk0nBfZ8LZkStKA==", + "dev": true, + "requires": { + "browserslist": "^4.22.2", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-url": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-6.0.1.tgz", + "integrity": "sha512-jEXL15tXSvbjm0yzUV7FBiEXwhIa9H88JOXDGQzmcWoB4mSjZIsmtto066s2iW9FYuIrIF4k04HA2BKAOpbsaQ==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-whitespace": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-6.0.1.tgz", + "integrity": "sha512-76i3NpWf6bB8UHlVuLRxG4zW2YykF9CTEcq/9LGAiz2qBuX5cBStadkk0jSkg9a9TCIXbMQz7yzrygKoCW9JuA==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-ordered-values": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-6.0.1.tgz", + "integrity": "sha512-XXbb1O/MW9HdEhnBxitZpPFbIvDgbo9NK4c/5bOfiKpnIGZDoL2xd7/e6jW5DYLsWxBbs+1nZEnVgnjnlFViaA==", + "dev": true, + "requires": { + "cssnano-utils": "^4.0.1", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-reduce-initial": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-6.0.2.tgz", + "integrity": "sha512-YGKalhNlCLcjcLvjU5nF8FyeCTkCO5UtvJEt0hrPZVCTtRLSOH4z00T1UntQPj4dUmIYZgMj8qK77JbSX95hSw==", + "dev": true, + "requires": { + "browserslist": "^4.22.2", + "caniuse-api": "^3.0.0" + } + }, + "postcss-reduce-transforms": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-6.0.1.tgz", + "integrity": "sha512-fUbV81OkUe75JM+VYO1gr/IoA2b/dRiH6HvMwhrIBSUrxq3jNZQZitSnugcTLDi1KkQh1eR/zi+iyxviUNBkcQ==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-svgo": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-6.0.2.tgz", + "integrity": "sha512-IH5R9SjkTkh0kfFOQDImyy1+mTCb+E830+9SV1O+AaDcoHTvfsvt6WwJeo7KwcHbFnevZVCsXhDmjFiGVuwqFQ==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0", + "svgo": "^3.2.0" + } + }, + "postcss-unique-selectors": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-6.0.2.tgz", + "integrity": "sha512-8IZGQ94nechdG7Y9Sh9FlIY2b4uS8/k8kdKRX040XHsS3B6d1HrJAkXrBSsSu4SuARruSsUjW3nlSw8BHkaAYQ==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.15" + } + }, + "schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + } + }, + "stylehacks": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-6.0.2.tgz", + "integrity": "sha512-00zvJGnCu64EpMjX8b5iCZ3us2Ptyw8+toEkb92VdmkEaRaSGBNKAoK6aWZckhXxmQP8zWiTaFaiMGIU8Ve8sg==", + "dev": true, + "requires": { + "browserslist": "^4.22.2", + "postcss-selector-parser": "^6.0.15" + } + }, + "svgo": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.2.0.tgz", + "integrity": "sha512-4PP6CMW/V7l/GmKRKzsLR8xxjdHTV4IMvhTnpuHwwBazSIlw5W/5SmPjN8Dwyt7lKbSJrRDgp4t9ph0HgChFBQ==", + "dev": true, + "requires": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^5.1.0", + "css-tree": "^2.3.1", + "css-what": "^6.1.0", + "csso": "^5.0.5", + "picocolors": "^1.0.0" + } + } + } + }, + "css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "requires": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + } + }, + "css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "requires": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + } + }, + "css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==" + }, + "cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==" + }, + "cssnano": { + "version": "5.1.14", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.14.tgz", + "integrity": "sha512-Oou7ihiTocbKqi0J1bB+TRJIQX5RMR3JghA8hcWSw9mjBLQ5Y3RWqEDoYG3sRNlAbCIXpqMoZGbq5KDR3vdzgw==", + "requires": { + "cssnano-preset-default": "^5.2.13", + "lilconfig": "^2.0.3", + "yaml": "^1.10.2" + } + }, + "cssnano-preset-advanced": { + "version": "5.3.10", + "resolved": "https://registry.npmjs.org/cssnano-preset-advanced/-/cssnano-preset-advanced-5.3.10.tgz", + "integrity": "sha512-fnYJyCS9jgMU+cmHO1rPSPf9axbQyD7iUhLO5Df6O4G+fKIOMps+ZbU0PdGFejFBBZ3Pftf18fn1eG7MAPUSWQ==", + "requires": { + "autoprefixer": "^10.4.12", + "cssnano-preset-default": "^5.2.14", + "postcss-discard-unused": "^5.1.0", + "postcss-merge-idents": "^5.1.1", + "postcss-reduce-idents": "^5.2.0", + "postcss-zindex": "^5.1.0" + } + }, + "cssnano-preset-default": { + "version": "5.2.14", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.14.tgz", + "integrity": "sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A==", + "requires": { + "css-declaration-sorter": "^6.3.1", + "cssnano-utils": "^3.1.0", + "postcss-calc": "^8.2.3", + "postcss-colormin": "^5.3.1", + "postcss-convert-values": "^5.1.3", + "postcss-discard-comments": "^5.1.2", + "postcss-discard-duplicates": "^5.1.0", + "postcss-discard-empty": "^5.1.1", + "postcss-discard-overridden": "^5.1.0", + "postcss-merge-longhand": "^5.1.7", + "postcss-merge-rules": "^5.1.4", + "postcss-minify-font-values": "^5.1.0", + "postcss-minify-gradients": "^5.1.1", + "postcss-minify-params": "^5.1.4", + "postcss-minify-selectors": "^5.2.1", + "postcss-normalize-charset": "^5.1.0", + "postcss-normalize-display-values": "^5.1.0", + "postcss-normalize-positions": "^5.1.1", + "postcss-normalize-repeat-style": "^5.1.1", + "postcss-normalize-string": "^5.1.0", + "postcss-normalize-timing-functions": "^5.1.0", + "postcss-normalize-unicode": "^5.1.1", + "postcss-normalize-url": "^5.1.0", + "postcss-normalize-whitespace": "^5.1.1", + "postcss-ordered-values": "^5.1.3", + "postcss-reduce-initial": "^5.1.2", + "postcss-reduce-transforms": "^5.1.0", + "postcss-svgo": "^5.1.0", + "postcss-unique-selectors": "^5.1.1" + } + }, + "cssnano-utils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz", + "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==" + }, + "csso": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "requires": { + "css-tree": "^1.1.2" + } + }, + "cssom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", + "dev": true + }, + "cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "requires": { + "cssom": "~0.3.6" + }, + "dependencies": { + "cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + } + } + }, + "csstype": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", + "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" + }, + "cytoscape": { + "version": "3.25.0", + "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.25.0.tgz", + "integrity": "sha512-7MW3Iz57mCUo6JQCho6CmPBCbTlJr7LzyEtIkutG255HLVd4XuBg2I9BkTZLI/e4HoaOB/BiAzXuQybQ95+r9Q==", + "requires": { + "heap": "^0.2.6", + "lodash": "^4.17.21" + } + }, + "cytoscape-cose-bilkent": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz", + "integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==", + "requires": { + "cose-base": "^1.0.0" + } + }, + "cytoscape-fcose": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz", + "integrity": "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==", + "requires": { + "cose-base": "^2.2.0" + }, + "dependencies": { + "cose-base": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-2.2.0.tgz", + "integrity": "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==", + "requires": { + "layout-base": "^2.0.0" + } + }, + "layout-base": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-2.0.1.tgz", + "integrity": "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==" + } + } + }, + "d3": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.8.5.tgz", + "integrity": "sha512-JgoahDG51ncUfJu6wX/1vWQEqOflgXyl4MaHqlcSruTez7yhaRKR9i8VjjcQGeS2en/jnFivXuaIMnseMMt0XA==", + "requires": { + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "4", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" + } + }, + "d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "requires": { + "internmap": "1 - 2" + } + }, + "d3-axis": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==" + }, + "d3-brush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", + "requires": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" + } + }, + "d3-chord": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", + "requires": { + "d3-path": "1 - 3" + } + }, + "d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==" + }, + "d3-contour": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", + "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", + "requires": { + "d3-array": "^3.2.0" + } + }, + "d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", + "requires": { + "delaunator": "5" + } + }, + "d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==" + }, + "d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "requires": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + } + }, + "d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "requires": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, + "dependencies": { + "commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==" + } + } + }, + "d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==" + }, + "d3-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", + "requires": { + "d3-dsv": "1 - 3" + } + }, + "d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "requires": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + } + }, + "d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==" + }, + "d3-geo": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.0.tgz", + "integrity": "sha512-JEo5HxXDdDYXCaWdwLRt79y7giK8SbhZJbFWXqbRTolCHFI5jRqteLzCsq51NKbUoX0PjBVSohxrx+NoOUujYA==", + "requires": { + "d3-array": "2.5.0 - 3" + } + }, + "d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==" + }, + "d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "requires": { + "d3-color": "1 - 3" + } + }, + "d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==" + }, + "d3-polygon": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", + "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==" + }, + "d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==" + }, + "d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==" + }, + "d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "requires": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + } + }, + "d3-scale-chromatic": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz", + "integrity": "sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g==", + "requires": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + } + }, + "d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==" + }, + "d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "requires": { + "d3-path": "^3.1.0" + } + }, + "d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "requires": { + "d3-array": "2 - 3" + } + }, + "d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "requires": { + "d3-time": "1 - 3" + } + }, + "d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==" + }, + "d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "requires": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + } + }, + "d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "requires": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + } + }, + "dagre-d3-es": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.9.tgz", + "integrity": "sha512-rYR4QfVmy+sR44IBDvVtcAmOReGBvRCWDpO2QjYwqgh9yijw6eSHBqaPG/LIOEy7aBsniLvtMW6pg19qJhq60w==", + "requires": { + "d3": "^7.8.2", + "lodash-es": "^4.17.21" + } + }, + "damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", + "dev": true + }, + "data-urls": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", + "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", + "dev": true, + "requires": { + "abab": "^2.0.6", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0" + } + }, + "dayjs": { + "version": "1.11.8", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.8.tgz", + "integrity": "sha512-LcgxzFoWMEPO7ggRv1Y2N31hUf2R0Vj7fuy/m+Bg1K8rr+KAs1AEy4y9jd5DXe8pbHgX+srkHNS7TH6Q6ZhYeQ==" + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "decimal.js": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", + "dev": true + }, + "decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "requires": { + "mimic-response": "^3.1.0" + }, + "dependencies": { + "mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==" + } + } + }, + "dedent": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", + "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", + "dev": true + }, + "deep-eql": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", + "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", + "dev": true, + "requires": { + "type-detect": "^4.0.0" + } + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" + }, + "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==" + }, + "deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==" + }, + "default-gateway": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", + "requires": { + "execa": "^5.0.0" + } + }, + "defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "requires": { + "clone": "^1.0.2" + } + }, + "defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==" + }, + "define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==" + }, + "define-properties": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "requires": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + } + }, + "del": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-6.1.1.tgz", + "integrity": "sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==", + "requires": { + "globby": "^11.0.1", + "graceful-fs": "^4.2.4", + "is-glob": "^4.0.1", + "is-path-cwd": "^2.2.0", + "is-path-inside": "^3.0.2", + "p-map": "^4.0.0", + "rimraf": "^3.0.2", + "slash": "^3.0.0" + }, + "dependencies": { + "fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + } + } + }, + "delaunator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.0.tgz", + "integrity": "sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==", + "requires": { + "robust-predicates": "^3.0.0" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" + }, + "detab": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detab/-/detab-2.0.4.tgz", + "integrity": "sha512-8zdsQA5bIkoRECvCrNKPla84lyoR7DSAyf7p0YgXzBO9PDJx8KntPUay7NS6yp+KdxdVtiE5SpHKtbp2ZQyA9g==", + "requires": { + "repeat-string": "^1.5.4" + } + }, + "detect-libc": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", + "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==" + }, + "detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true + }, + "detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==" + }, + "detect-port": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.5.1.tgz", + "integrity": "sha512-aBzdj76lueB6uUst5iAs7+0H/oOjqI5D16XUWxlWMIMROhcM0rfsNVk93zTngq1dDNpoXRr++Sus7ETAExppAQ==", + "requires": { + "address": "^1.0.1", + "debug": "4" + } + }, + "detect-port-alt": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz", + "integrity": "sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==", + "requires": { + "address": "^1.0.1", + "debug": "^2.6.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + } + } + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "requires": { + "path-type": "^4.0.0" + } + }, + "dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==" + }, + "dns-packet": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.4.0.tgz", + "integrity": "sha512-EgqGeaBB8hLiHLZtp/IbaDQTL8pZ0+IvwzSHA6d7VyMDM+B9hgddEMa9xjK5oYnw0ci0JQ6g2XCD7/f6cafU6g==", + "requires": { + "@leichtgewicht/ip-codec": "^2.0.1" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "requires": { + "utila": "~0.4" + } + }, + "dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + } + }, + "domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==" + }, + "domexception": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", + "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", + "dev": true, + "requires": { + "webidl-conversions": "^7.0.0" + } + }, + "domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "requires": { + "domelementtype": "^2.2.0" + } + }, + "dompurify": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.3.tgz", + "integrity": "sha512-q6QaLcakcRjebxjg8/+NP+h0rPfatOgOzc46Fst9VAA3jF2ApfKBNKMzdP4DYTqtUMXSCd5pRS/8Po/OmoCHZQ==" + }, + "domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "requires": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + } + }, + "dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "requires": { + "is-obj": "^2.0.0" + }, + "dependencies": { + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==" + } + } + }, + "dotenv": { + "version": "16.3.2", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.2.tgz", + "integrity": "sha512-HTlk5nmhkm8F6JcdXvHIzaorzCoziNQT9mGxLPVXW8wJF1TiGSL60ZGB4gHWabHOaMmWmhvk2/lPHfnBiT78AQ==", + "dev": true + }, + "dotenv-expand": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-10.0.0.tgz", + "integrity": "sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==", + "dev": true + }, + "duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" + }, + "duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "requires": { + "readable-stream": "^2.0.2" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "requires": { + "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" + } + }, + "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==" + }, + "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==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "ejs": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.8.tgz", + "integrity": "sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==", + "dev": true, + "requires": { + "jake": "^10.8.5" + } + }, + "electron-to-chromium": { + "version": "1.4.651", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.651.tgz", + "integrity": "sha512-jjks7Xx+4I7dslwsbaFocSwqBbGHQmuXBJUK9QBZTIrzPq3pzn6Uf2szFSP728FtLYE3ldiccmlkOM/zhGKCpA==" + }, + "elkjs": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/elkjs/-/elkjs-0.8.2.tgz", + "integrity": "sha512-L6uRgvZTH+4OF5NE/MBbzQx/WYpru1xCBE9respNj6qznEewGUIfhzmm7horWWxbNO2M0WckQypGctR8lH79xQ==" + }, + "emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==" + }, + "emoticon": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/emoticon/-/emoticon-3.2.0.tgz", + "integrity": "sha512-SNujglcLTTg+lDAcApPNgEdudaqQFiAbJCqzjNxJkvN9vAwCGi0uu8IUVvx+f16h+V44KCY6Y2yboroc9pilHg==" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" + }, + "encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "optional": true, + "requires": { + "iconv-lite": "^0.6.2" + } + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "requires": { + "once": "^1.4.0" + } + }, + "enhanced-resolve": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", + "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", + "requires": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + } + }, + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.1" + } + }, + "entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" + }, + "env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "optional": true + }, + "err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "optional": true + }, + "errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dev": true, + "optional": true, + "requires": { + "prr": "~1.0.1" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.4.tgz", + "integrity": "sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.1.3", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.2", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.4.3", + "safe-regex-test": "^1.0.0", + "string.prototype.trimend": "^1.0.5", + "string.prototype.trimstart": "^1.0.5", + "unbox-primitive": "^1.0.2" + } + }, + "es-module-lexer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.4.1.tgz", + "integrity": "sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==" + }, + "es-shim-unscopables": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", + "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es6-object-assign": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", + "integrity": "sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw==" + }, + "esbuild": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", + "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", + "dev": true, + "requires": { + "@esbuild/aix-ppc64": "0.19.12", + "@esbuild/android-arm": "0.19.12", + "@esbuild/android-arm64": "0.19.12", + "@esbuild/android-x64": "0.19.12", + "@esbuild/darwin-arm64": "0.19.12", + "@esbuild/darwin-x64": "0.19.12", + "@esbuild/freebsd-arm64": "0.19.12", + "@esbuild/freebsd-x64": "0.19.12", + "@esbuild/linux-arm": "0.19.12", + "@esbuild/linux-arm64": "0.19.12", + "@esbuild/linux-ia32": "0.19.12", + "@esbuild/linux-loong64": "0.19.12", + "@esbuild/linux-mips64el": "0.19.12", + "@esbuild/linux-ppc64": "0.19.12", + "@esbuild/linux-riscv64": "0.19.12", + "@esbuild/linux-s390x": "0.19.12", + "@esbuild/linux-x64": "0.19.12", + "@esbuild/netbsd-x64": "0.19.12", + "@esbuild/openbsd-x64": "0.19.12", + "@esbuild/sunos-x64": "0.19.12", + "@esbuild/win32-arm64": "0.19.12", + "@esbuild/win32-ia32": "0.19.12", + "@esbuild/win32-x64": "0.19.12" + } + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" + }, + "escape-goat": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", + "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" + }, + "escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dev": true, + "requires": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "source-map": "~0.6.1" + } + }, + "eslint": { + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.48.0.tgz", + "integrity": "sha512-sb6DLeIuRXxeM1YljSe1KEx9/YYeZFQWcV8Rq9HfigmdDEugjLEVEa1ozDjL6YDjBpQHPJxJzze+alxi4T3OLg==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "8.48.0", + "@humanwhocodes/config-array": "^0.11.10", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "dependencies": { + "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 + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + }, + "globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + } + } + }, + "eslint-config-prettier": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", + "dev": true + }, + "eslint-config-react-app": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-7.0.1.tgz", + "integrity": "sha512-K6rNzvkIeHaTd8m/QEh1Zko0KI7BACWkkneSs6s9cKZC/J27X3eZR6Upt1jkmZ/4FK+XUOPPxMEN7+lbUXfSlA==", + "dev": true, + "requires": { + "@babel/core": "^7.16.0", + "@babel/eslint-parser": "^7.16.3", + "@rushstack/eslint-patch": "^1.1.0", + "@typescript-eslint/eslint-plugin": "^5.5.0", + "@typescript-eslint/parser": "^5.5.0", + "babel-preset-react-app": "^10.0.1", + "confusing-browser-globals": "^1.0.11", + "eslint-plugin-flowtype": "^8.0.3", + "eslint-plugin-import": "^2.25.3", + "eslint-plugin-jest": "^25.3.0", + "eslint-plugin-jsx-a11y": "^6.5.1", + "eslint-plugin-react": "^7.27.1", + "eslint-plugin-react-hooks": "^4.3.0", + "eslint-plugin-testing-library": "^5.0.1" + }, + "dependencies": { + "@typescript-eslint/eslint-plugin": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", + "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", + "dev": true, + "requires": { + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/type-utils": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/parser": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", + "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "debug": "^4.3.4" + } + }, + "@typescript-eslint/type-utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", + "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", + "dev": true, + "requires": { + "@typescript-eslint/typescript-estree": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + } + }, + "eslint-plugin-jest": { + "version": "25.7.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-25.7.0.tgz", + "integrity": "sha512-PWLUEXeeF7C9QGKqvdSbzLOiLTx+bno7/HC9eefePfEb257QFHg7ye3dh80AZVkaa/RQsBB1Q/ORQvg2X7F0NQ==", + "dev": true, + "requires": { + "@typescript-eslint/experimental-utils": "^5.0.0" + } + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "eslint-import-resolver-node": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz", + "integrity": "sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==", + "dev": true, + "requires": { + "debug": "^3.2.7", + "is-core-module": "^2.11.0", + "resolve": "^1.22.1" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "eslint-module-utils": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz", + "integrity": "sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==", + "dev": true, + "requires": { + "debug": "^3.2.7" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "eslint-plugin-flowtype": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-8.0.3.tgz", + "integrity": "sha512-dX8l6qUL6O+fYPtpNRideCFSpmWOUVx5QcaGLVqe/vlDiBSe4vYljDWDETwnyFzpl7By/WVIu6rcrniCgH9BqQ==", + "dev": true, + "requires": { + "lodash": "^4.17.21", + "string-natural-compare": "^3.0.1" + } + }, + "eslint-plugin-import": { + "version": "2.27.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz", + "integrity": "sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==", + "dev": true, + "requires": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "array.prototype.flatmap": "^1.3.1", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.7", + "eslint-module-utils": "^2.7.4", + "has": "^1.0.3", + "is-core-module": "^2.11.0", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.values": "^1.1.6", + "resolve": "^1.22.1", + "semver": "^6.3.0", + "tsconfig-paths": "^3.14.1" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "eslint-plugin-jsx-a11y": { + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.6.1.tgz", + "integrity": "sha512-sXgFVNHiWffBq23uiS/JaP6eVR622DqwB4yTzKvGZGcPq6/yZ3WmOZfuBks/vHWo9GaFOqC2ZK4i6+C35knx7Q==", + "dev": true, + "requires": { + "@babel/runtime": "^7.18.9", + "aria-query": "^4.2.2", + "array-includes": "^3.1.5", + "ast-types-flow": "^0.0.7", + "axe-core": "^4.4.3", + "axobject-query": "^2.2.0", + "damerau-levenshtein": "^1.0.8", + "emoji-regex": "^9.2.2", + "has": "^1.0.3", + "jsx-ast-utils": "^3.3.2", + "language-tags": "^1.0.5", + "minimatch": "^3.1.2", + "semver": "^6.3.0" + }, + "dependencies": { + "aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.10.2", + "@babel/runtime-corejs3": "^7.10.2" + } + }, + "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 + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "eslint-plugin-prettier": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz", + "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==", + "dev": true, + "requires": { + "prettier-linter-helpers": "^1.0.0" + } + }, + "eslint-plugin-react": { + "version": "7.32.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.32.2.tgz", + "integrity": "sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg==", + "dev": true, + "requires": { + "array-includes": "^3.1.6", + "array.prototype.flatmap": "^1.3.1", + "array.prototype.tosorted": "^1.1.1", + "doctrine": "^2.1.0", + "estraverse": "^5.3.0", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.6", + "object.fromentries": "^2.0.6", + "object.hasown": "^1.1.2", + "object.values": "^1.1.6", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.4", + "semver": "^6.3.0", + "string.prototype.matchall": "^4.0.8" + }, + "dependencies": { + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "resolve": { + "version": "2.0.0-next.4", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", + "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==", + "dev": true, + "requires": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "eslint-plugin-react-hooks": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", + "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", + "dev": true + }, + "eslint-plugin-testing-library": { + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-5.9.1.tgz", + "integrity": "sha512-6BQp3tmb79jLLasPHJmy8DnxREe+2Pgf7L+7o09TSWPfdqqtQfRZmZNetr5mOs3yqZk/MRNxpN3RUpJe0wB4LQ==", + "dev": true, + "requires": { + "@typescript-eslint/utils": "^5.13.0" + } + }, + "eslint-plugin-unicorn": { + "version": "52.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-52.0.0.tgz", + "integrity": "sha512-1Yzm7/m+0R4djH0tjDjfVei/ju2w3AzUGjG6q8JnuNIL5xIwsflyCooW5sfBvQp2pMYQFSWWCFONsjCax1EHng==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.22.20", + "@eslint-community/eslint-utils": "^4.4.0", + "@eslint/eslintrc": "^2.1.4", + "ci-info": "^4.0.0", + "clean-regexp": "^1.0.0", + "core-js-compat": "^3.34.0", + "esquery": "^1.5.0", + "indent-string": "^4.0.0", + "is-builtin-module": "^3.2.1", + "jsesc": "^3.0.2", + "pluralize": "^8.0.0", + "read-pkg-up": "^7.0.1", + "regexp-tree": "^0.1.27", + "regjsparser": "^0.10.0", + "semver": "^7.5.4", + "strip-indent": "^3.0.0" + }, + "dependencies": { + "ci-info": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.0.0.tgz", + "integrity": "sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg==", + "dev": true + }, + "jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "dev": true + }, + "regjsparser": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.10.0.tgz", + "integrity": "sha512-qx+xQGZVsy55CH0a1hiVwHmqjLryfh7wQyF5HO07XJ9f7dQMY/gPQHhlyDkIzJKC+x2fUCpCcUODUUUFrm7SHA==", + "dev": true, + "requires": { + "jsesc": "~0.5.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "dev": true + } + } + }, + "semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "eslint-plugin-vitest": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-vitest/-/eslint-plugin-vitest-0.5.4.tgz", + "integrity": "sha512-um+odCkccAHU53WdKAw39MY61+1x990uXjSPguUCq3VcEHdqJrOb8OTMrbYlY6f9jAKx7x98kLVlIe3RJeJqoQ==", + "dev": true, + "requires": { + "@typescript-eslint/utils": "^7.7.1" + }, + "dependencies": { + "@typescript-eslint/scope-manager": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.8.0.tgz", + "integrity": "sha512-viEmZ1LmwsGcnr85gIq+FCYI7nO90DVbE37/ll51hjv9aG+YZMb4WDE2fyWpUR4O/UrhGRpYXK/XajcGTk2B8g==", + "dev": true, + "requires": { + "@typescript-eslint/types": "7.8.0", + "@typescript-eslint/visitor-keys": "7.8.0" + } + }, + "@typescript-eslint/types": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.8.0.tgz", + "integrity": "sha512-wf0peJ+ZGlcH+2ZS23aJbOv+ztjeeP8uQ9GgwMJGVLx/Nj9CJt17GWgWWoSmoRVKAX2X+7fzEnAjxdvK2gqCLw==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.8.0.tgz", + "integrity": "sha512-5pfUCOwK5yjPaJQNy44prjCwtr981dO8Qo9J9PwYXZ0MosgAbfEMB008dJ5sNo3+/BN6ytBPuSvXUg9SAqB0dg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "7.8.0", + "@typescript-eslint/visitor-keys": "7.8.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + } + }, + "@typescript-eslint/utils": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.8.0.tgz", + "integrity": "sha512-L0yFqOCflVqXxiZyXrDr80lnahQfSOfc9ELAAZ75sqicqp2i36kEZZGuUymHNFoYOqxRT05up760b4iGsl02nQ==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.15", + "@types/semver": "^7.5.8", + "@typescript-eslint/scope-manager": "7.8.0", + "@typescript-eslint/types": "7.8.0", + "@typescript-eslint/typescript-estree": "7.8.0", + "semver": "^7.6.0" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.8.0.tgz", + "integrity": "sha512-q4/gibTNBQNA0lGyYQCmWRS5D15n8rXh4QjK3KV+MBPlTYHpfBUT3D3PaPR/HeNiI9W6R7FvlkcGhNyAoP+caA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "7.8.0", + "eslint-visitor-keys": "^3.4.3" + } + }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, + "minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "dependencies": { + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" + } + } + }, + "eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^2.0.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true + } + } + }, + "eslint-visitor-keys": { + "version": "3.4.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==" + }, + "espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "requires": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + }, + "esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "requires": { + "estraverse": "^5.2.0" + } + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" + }, + "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 + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" + }, + "eta": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/eta/-/eta-2.0.1.tgz", + "integrity": "sha512-46E2qDPDm7QA+usjffUWz9KfXsxVZclPOuKsXs4ZWZdI/X1wpDF7AO424pt7fdYohCzWsIkXAhNGXSlwo5naAg==" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" + }, + "eval": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/eval/-/eval-0.1.8.tgz", + "integrity": "sha512-EzV94NYKoO09GLXGjXj9JIlXijVck4ONSr5wiCWDvhsvj5jxSrzTmRU/9C1DyB6uToszLs8aifA6NQ7lEQdvFw==", + "requires": { + "@types/node": "*", + "require-like": ">= 0.1.1" + } + }, + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" + }, + "exceljs": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/exceljs/-/exceljs-4.3.0.tgz", + "integrity": "sha512-hTAeo5b5TPvf8Z02I2sKIT4kSfCnOO2bCxYX8ABqODCdAjppI3gI9VYiGCQQYVcBaBSKlFDMKlAQRqC+kV9O8w==", + "requires": { + "archiver": "^5.0.0", + "dayjs": "^1.8.34", + "fast-csv": "^4.3.1", + "jszip": "^3.5.0", + "readable-stream": "^3.6.0", + "saxes": "^5.0.1", + "tmp": "^0.2.0", + "unzipper": "^0.10.11", + "uuid": "^8.3.0" + } + }, + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true + }, + "expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "dev": true, + "optional": true + }, + "expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "requires": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + } + }, + "express": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "requires": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "requires": { + "is-extendable": "^0.1.0" + } + }, + "fast-csv": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/fast-csv/-/fast-csv-4.3.6.tgz", + "integrity": "sha512-2RNSpuwwsJGP0frGsOmTb9oUF+VkFSM4SyLTDgwf2ciHWTarN0lQTC+F2f/t5J9QjW+c65VFIAAu85GsvMIusw==", + "requires": { + "@fast-csv/format": "4.3.5", + "@fast-csv/parse": "4.3.6" + } + }, + "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==" + }, + "fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "dev": true + }, + "fast-glob": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", + "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" + }, + "fast-url-parser": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz", + "integrity": "sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ==", + "requires": { + "punycode": "^1.3.2" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==" + } + } + }, + "fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "requires": { + "reusify": "^1.0.4" + } + }, + "faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "requires": { + "websocket-driver": ">=0.5.1" + } + }, + "fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "requires": { + "bser": "2.1.1" + } + }, + "fbemitter": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/fbemitter/-/fbemitter-3.0.0.tgz", + "integrity": "sha512-KWKaceCwKQU0+HPoop6gn4eOHk50bBv/VxjJtGMfwmJt3D29JpN4H4eisCtIPA+a8GVBam+ldMMpMjJUvpDyHw==", + "requires": { + "fbjs": "^3.0.0" + } + }, + "fbjs": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-3.0.5.tgz", + "integrity": "sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg==", + "requires": { + "cross-fetch": "^3.1.5", + "fbjs-css-vars": "^1.0.0", + "loose-envify": "^1.0.0", + "object-assign": "^4.1.0", + "promise": "^7.1.1", + "setimmediate": "^1.0.5", + "ua-parser-js": "^1.0.35" + } + }, + "fbjs-css-vars": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz", + "integrity": "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==" + }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "requires": { + "pend": "~1.2.0" + } + }, + "feed": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/feed/-/feed-4.2.2.tgz", + "integrity": "sha512-u5/sxGfiMfZNtJ3OvQpXcvotFpYkL0n9u9mM2vkui2nGo8b4wvDkJ8gAkYqbA8QpGyFCv3RK0Z+Iv+9veCS9bQ==", + "requires": { + "xml-js": "^1.6.11" + } + }, + "fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "dev": true + }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, + "file-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", + "requires": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "dependencies": { + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dev": true, + "requires": { + "minimatch": "^5.0.1" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, + "filesize": { + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz", + "integrity": "sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==" + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + } + } + }, + "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==", + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true + }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true + }, + "flux": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/flux/-/flux-4.0.4.tgz", + "integrity": "sha512-NCj3XlayA2UsapRpM7va6wU1+9rE5FIL7qoMcmxWHRzbp0yujihMBm9BBHZ1MDIk5h5o2Bl6eGiCe8rYELAmYw==", + "requires": { + "fbemitter": "^3.0.0", + "fbjs": "^3.0.1" + } + }, + "follow-redirects": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", + "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==" + }, + "for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "requires": { + "is-callable": "^1.1.3" + } + }, + "fork-ts-checker-webpack-plugin": { + "version": "7.2.13", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-7.2.13.tgz", + "integrity": "sha512-fR3WRkOb4bQdWB/y7ssDUlVdrclvwtyCUIHCfivAoYxq9dF7XfrDKbMdZIfwJ7hxIAqkYSGeU7lLJE6xrxIBdg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.16.7", + "chalk": "^4.1.2", + "chokidar": "^3.5.3", + "cosmiconfig": "^7.0.1", + "deepmerge": "^4.2.2", + "fs-extra": "^10.0.0", + "memfs": "^3.4.1", + "minimatch": "^3.0.4", + "node-abort-controller": "^3.0.1", + "schema-utils": "^3.1.1", + "semver": "^7.3.5", + "tapable": "^2.2.1" + }, + "dependencies": { + "schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" + }, + "fp-ts": { + "version": "2.16.5", + "resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-2.16.5.tgz", + "integrity": "sha512-N8T8PwMSeTKKtkm9lkj/zSTAnPC/aJIIrQhnHxxkL0KLsRCNUPANksJOlMXxcKKCo7H1ORP3No9EMD+fP0tsdA==" + }, + "fraction.js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", + "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" + }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, + "fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "requires": { + "minipass": "^3.0.0" + } + }, + "fs-monkey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", + "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==" + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "optional": true + }, + "fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "requires": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + }, + "dependencies": { + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "requires": { + "minimist": "^1.2.6" + } + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + } + }, + "functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true + }, + "gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "requires": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + } + }, + "generic-names": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/generic-names/-/generic-names-4.0.0.tgz", + "integrity": "sha512-ySFolZQfw9FoDb3ed9d80Cm9f0+r7qj+HJkWjeD9RBfpxEVTlVhol+gvaQB/78WbwYfbnNh8nWHHBSlg072y6A==", + "dev": true, + "requires": { + "loader-utils": "^3.2.0" + }, + "dependencies": { + "loader-utils": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz", + "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==", + "dev": true + } + } + }, + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==" + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true + }, + "get-intrinsic": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", + "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + } + }, + "get-own-enumerable-property-symbols": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", + "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==" + }, + "get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==" + }, + "get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + } + }, + "github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "dev": true, + "optional": true + }, + "github-slugger": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.5.0.tgz", + "integrity": "sha512-wIh+gKBI9Nshz2o46B0B3f5k/W+WI9ZAv6y5Dn5WJ5SK1t0TnDimB4WE5rmTD05ZAIn8HALCZVmCsvj0w0v0lw==" + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "requires": { + "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" + }, + "dependencies": { + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "requires": { + "is-glob": "^4.0.1" + } + }, + "glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" + }, + "global-dirs": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", + "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==", + "requires": { + "ini": "2.0.0" + }, + "dependencies": { + "ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==" + } + } + }, + "global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "requires": { + "global-prefix": "^3.0.0" + } + }, + "global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "requires": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "dependencies": { + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" + }, + "globby": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.1.tgz", + "integrity": "sha512-sSs4inE1FB2YQiymcmTv6NWENryABjUNPeWhOvmn4SjtKybglsyPZxFB3U1/+L1bYi0rNZDqCLlHyLYDl1Pq5A==", + "dev": true, + "requires": { + "@types/glob": "^7.1.1", + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.0.3", + "glob": "^7.1.3", + "ignore": "^5.1.1", + "merge2": "^1.2.3", + "slash": "^3.0.0" + } + }, + "gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "requires": { + "get-intrinsic": "^1.1.3" + } + }, + "graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" + }, + "graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "gray-matter": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", + "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==", + "requires": { + "js-yaml": "^3.13.1", + "kind-of": "^6.0.2", + "section-matter": "^1.0.0", + "strip-bom-string": "^1.0.0" + } + }, + "gtfs-realtime-bindings": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/gtfs-realtime-bindings/-/gtfs-realtime-bindings-1.1.1.tgz", + "integrity": "sha512-+k8+/MmiBmUUWlASs4CeTkV+Qyz/FgbZxXdg9rDU62XRfJOpRaRe+nKWCGKse965jffVZ0tIu1K+R7hRvjSLfQ==", + "requires": { + "protobufjs": "^7.1.2", + "protobufjs-cli": "^1.0.2" + } + }, + "gzip-size": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", + "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", + "requires": { + "duplexer": "^0.1.2" + } + }, + "handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==" + }, + "harmony-reflect": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz", + "integrity": "sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "requires": { + "get-intrinsic": "^1.1.1" + } + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "requires": { + "has-symbols": "^1.0.2" + } + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" + }, + "has-yarn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", + "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==" + }, + "hast-to-hyperscript": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/hast-to-hyperscript/-/hast-to-hyperscript-9.0.1.tgz", + "integrity": "sha512-zQgLKqF+O2F72S1aa4y2ivxzSlko3MAvxkwG8ehGmNiqd98BIN3JM1rAJPmplEyLmGLO2QZYJtIneOSZ2YbJuA==", + "requires": { + "@types/unist": "^2.0.3", + "comma-separated-tokens": "^1.0.0", + "property-information": "^5.3.0", + "space-separated-tokens": "^1.0.0", + "style-to-object": "^0.3.0", + "unist-util-is": "^4.0.0", + "web-namespaces": "^1.0.0" + } + }, + "hast-util-from-parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-6.0.1.tgz", + "integrity": "sha512-jeJUWiN5pSxW12Rh01smtVkZgZr33wBokLzKLwinYOUfSzm1Nl/c3GUGebDyOKjdsRgMvoVbV0VpAcpjF4NrJA==", + "requires": { + "@types/parse5": "^5.0.0", + "hastscript": "^6.0.0", + "property-information": "^5.0.0", + "vfile": "^4.0.0", + "vfile-location": "^3.2.0", + "web-namespaces": "^1.0.0" + }, + "dependencies": { + "@types/parse5": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@types/parse5/-/parse5-5.0.3.tgz", + "integrity": "sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw==" + } + } + }, + "hast-util-parse-selector": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz", + "integrity": "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==" + }, + "hast-util-raw": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-6.0.1.tgz", + "integrity": "sha512-ZMuiYA+UF7BXBtsTBNcLBF5HzXzkyE6MLzJnL605LKE8GJylNjGc4jjxazAHUtcwT5/CEt6afRKViYB4X66dig==", + "requires": { + "@types/hast": "^2.0.0", + "hast-util-from-parse5": "^6.0.0", + "hast-util-to-parse5": "^6.0.0", + "html-void-elements": "^1.0.0", + "parse5": "^6.0.0", + "unist-util-position": "^3.0.0", + "vfile": "^4.0.0", + "web-namespaces": "^1.0.0", + "xtend": "^4.0.0", + "zwitch": "^1.0.0" + }, + "dependencies": { + "parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" + } + } + }, + "hast-util-to-parse5": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-6.0.0.tgz", + "integrity": "sha512-Lu5m6Lgm/fWuz8eWnrKezHtVY83JeRGaNQ2kn9aJgqaxvVkFCZQBEhgodZUDUvoodgyROHDb3r5IxAEdl6suJQ==", + "requires": { + "hast-to-hyperscript": "^9.0.0", + "property-information": "^5.0.0", + "web-namespaces": "^1.0.0", + "xtend": "^4.0.0", + "zwitch": "^1.0.0" + } + }, + "hastscript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz", + "integrity": "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==", + "requires": { + "@types/hast": "^2.0.0", + "comma-separated-tokens": "^1.0.0", + "hast-util-parse-selector": "^2.0.0", + "property-information": "^5.0.0", + "space-separated-tokens": "^1.0.0" + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" + }, + "heap": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.7.tgz", + "integrity": "sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==" + }, + "history": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", + "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==", + "requires": { + "@babel/runtime": "^7.1.2", + "loose-envify": "^1.2.0", + "resolve-pathname": "^3.0.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0", + "value-equal": "^1.0.1" + } + }, + "hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "requires": { + "react-is": "^16.7.0" + }, + "dependencies": { + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + } + } + }, + "hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", + "requires": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "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" + } + }, + "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==" + }, + "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==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "html-encoding-sniffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "dev": true, + "requires": { + "whatwg-encoding": "^2.0.0" + } + }, + "html-entities": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", + "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==" + }, + "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 + }, + "html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", + "requires": { + "camel-case": "^4.1.2", + "clean-css": "^5.2.2", + "commander": "^8.3.0", + "he": "^1.2.0", + "param-case": "^3.0.4", + "relateurl": "^0.2.7", + "terser": "^5.10.0" + } + }, + "html-tags": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.2.0.tgz", + "integrity": "sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==" + }, + "html-void-elements": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-1.0.5.tgz", + "integrity": "sha512-uE/TxKuyNIcx44cIWnjr/rfIATDH7ZaOMmstu0CwhFG1Dunhlp4OC6/NMbhiwoq5BpW0ubi303qnEk/PZj614w==" + }, + "html-webpack-plugin": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz", + "integrity": "sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==", + "requires": { + "@types/html-minifier-terser": "^6.0.0", + "html-minifier-terser": "^6.0.2", + "lodash": "^4.17.21", + "pretty-error": "^4.0.0", + "tapable": "^2.0.0" + } + }, + "htmlparser2": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.1.tgz", + "integrity": "sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==", + "requires": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "entities": "^4.3.0" + }, + "dependencies": { + "dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "requires": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + } + }, + "domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "requires": { + "domelementtype": "^2.3.0" + } + }, + "domutils": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz", + "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==", + "requires": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.1" + } + }, + "entities": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", + "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==" + } + } + }, + "http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==" + }, + "http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==" + }, + "http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "requires": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + } + }, + "http-parser-js": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==" + }, + "http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "requires": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "requires": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + } + }, + "http-proxy-middleware": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", + "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "requires": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + } + }, + "http-server": { + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/http-server/-/http-server-14.1.1.tgz", + "integrity": "sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A==", + "dev": true, + "requires": { + "basic-auth": "^2.0.1", + "chalk": "^4.1.2", + "corser": "^2.0.1", + "he": "^1.2.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy": "^1.18.1", + "mime": "^1.6.0", + "minimist": "^1.2.6", + "opener": "^1.5.1", + "portfinder": "^1.0.28", + "secure-compare": "3.0.1", + "union": "~0.5.0", + "url-join": "^4.0.1" + } + }, + "https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==" + }, + "humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "optional": true, + "requires": { + "ms": "^2.0.0" + } + }, + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + }, + "icss-replace-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", + "integrity": "sha512-chIaY3Vh2mh2Q3RGXttaDIzeiPvaVXJ+C4DAh/w3c37SKZ/U6PGMmuicR2EQQp9bKG8zLMCl7I+PtIoOOPp8Gg==", + "dev": true + }, + "icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==" + }, + "identity-obj-proxy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz", + "integrity": "sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==", + "dev": true, + "requires": { + "harmony-reflect": "^1.4.6" + } + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, + "ignore": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", + "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==" + }, + "image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==", + "dev": true, + "optional": true + }, + "immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" + }, + "immer": { + "version": "9.0.19", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.19.tgz", + "integrity": "sha512-eY+Y0qcsB4TZKwgQzLaE/lqYMlKhv5J9dyd2RhhtGhNo2njPXDqU9XPfcNfa3MIDsdtZt5KlkIsirlo4dHsWdQ==" + }, + "immutable": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.5.tgz", + "integrity": "sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw==", + "dev": true + }, + "import-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-3.0.0.tgz", + "integrity": "sha512-4pnzH16plW+hgvRECbDWpQl3cqtvSofHWh44met7ESfZ8UZOWWddm8hEyDTqREJ9RbYHY8gi8DqmaelApoOGMg==", + "dev": true, + "requires": { + "import-from": "^3.0.0" + } + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "dependencies": { + "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==" + } + } + }, + "import-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-3.0.0.tgz", + "integrity": "sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ==", + "dev": true, + "requires": { + "resolve-from": "^5.0.0" + } + }, + "import-lazy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", + "integrity": "sha512-m7ZEHgtw69qOGw+jwxXkHlrlIPdTGkyh66zXZ1ajZbxkDBNjSY/LGbmjc7h0s2ELsUDTAhFr55TrPSSqJGPG0A==" + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==" + }, + "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==" + }, + "infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "optional": true + }, + "infima": { + "version": "0.2.0-alpha.43", + "resolved": "https://registry.npmjs.org/infima/-/infima-0.2.0-alpha.43.tgz", + "integrity": "sha512-2uw57LvUqW0rK/SWYnd/2rRfxNA5DDNOh33jxF7fy46VWoNhGxiUQyVZHbBMjQ33mQem0cjdDVwgWVAmlRfgyQ==" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, + "inline-style-parser": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", + "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==" + }, + "internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + } + }, + "internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==" + }, + "interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==" + }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "requires": { + "loose-envify": "^1.0.0" + } + }, + "ip": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", + "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==", + "optional": true + }, + "ipaddr.js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", + "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==" + }, + "is-alphabetical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", + "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==" + }, + "is-alphanumerical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", + "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", + "requires": { + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0" + } + }, + "is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + }, + "is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "requires": { + "has-bigints": "^1.0.1" + } + }, + "is-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==", + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-builtin-module": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", + "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", + "dev": true, + "requires": { + "builtin-modules": "^3.3.0" + } + }, + "is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==" + }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "requires": { + "ci-info": "^2.0.0" + }, + "dependencies": { + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" + } + } + }, + "is-core-module": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "requires": { + "has": "^1.0.3" + } + }, + "is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-decimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", + "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==" + }, + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==" + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==" + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" + }, + "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==" + }, + "is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true + }, + "is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-hexadecimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", + "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==" + }, + "is-installed-globally": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", + "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", + "requires": { + "global-dirs": "^3.0.0", + "is-path-inside": "^3.0.2" + } + }, + "is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true + }, + "is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", + "optional": true + }, + "is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", + "dev": true + }, + "is-nan": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", + "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + } + }, + "is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true + }, + "is-npm": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-5.0.0.tgz", + "integrity": "sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==" + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + }, + "is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==" + }, + "is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==" + }, + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==" + }, + "is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==" + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "requires": { + "isobject": "^3.0.1" + } + }, + "is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true + }, + "is-reference": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", + "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", + "dev": true, + "requires": { + "@types/estree": "*" + } + }, + "is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==" + }, + "is-root": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz", + "integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==" + }, + "is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" + } + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" + }, + "is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, + "is-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" + }, + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true + }, + "is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" + } + }, + "is-what": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", + "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==", + "dev": true + }, + "is-whitespace-character": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz", + "integrity": "sha512-SDweEzfIZM0SJV0EUga669UTKlmL0Pq8Lno0QDQsPnvECB3IM2aP0gdx5TrU0A01MAPfViaZiI2V1QMZLaKK5w==" + }, + "is-word-character": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-word-character/-/is-word-character-1.0.4.tgz", + "integrity": "sha512-5SMO8RVennx3nZrqtKwCGyyetPE9VDba5ugvKLaD4KopPG5kR4mQ7tNt/r7feL5yt5h3lpuBbIUmCOG2eSzXHA==" + }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "requires": { + "is-docker": "^2.0.0" + } + }, + "is-yarn-global": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", + "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==" + }, + "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 + }, + "istanbul-lib-instrument": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.1.tgz", + "integrity": "sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==", + "dev": true, + "requires": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "dependencies": { + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "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, + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "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, + "requires": { + "semver": "^7.5.3" + } + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + } + }, + "istanbul-reports": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", + "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", + "dev": true, + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, + "jake": { + "version": "10.8.5", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz", + "integrity": "sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==", + "dev": true, + "requires": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.1", + "minimatch": "^3.0.4" + } + }, + "jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "dev": true, + "requires": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + } + }, + "jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "requires": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + } + }, + "jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + } + }, + "jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "dev": true, + "requires": { + "detect-newline": "^3.0.0" + } + }, + "jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + } + }, + "jest-environment-jsdom": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz", + "integrity": "sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==", + "dev": true, + "requires": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/jsdom": "^20.0.0", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0", + "jsdom": "^20.0.0" + } + }, + "jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "requires": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + } + }, + "jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true + }, + "jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.3.2", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + } + }, + "jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "requires": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + } + }, + "jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + } + }, + "jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + } + }, + "jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + } + }, + "jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true + }, + "jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true + }, + "jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "dependencies": { + "resolve.exports": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "dev": true + } + } + }, + "jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, + "requires": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "dependencies": { + "source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + } + } + }, + "jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "requires": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + } + }, + "jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "requires": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "dependencies": { + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "requires": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + } + }, + "jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "dependencies": { + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true + } + } + }, + "jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "requires": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + } + }, + "jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "requires": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "dependencies": { + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "joi": { + "version": "17.7.1", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.7.1.tgz", + "integrity": "sha512-teoLhIvWE298R6AeJywcjR4sX2hHjB3/xJX4qPjg+gTg+c0mzUDsziYlqPmLomq9gVsfaMcgPaGc7VxtD/9StA==", + "requires": { + "@hapi/hoek": "^9.0.0", + "@hapi/topo": "^5.0.0", + "@sideway/address": "^4.1.3", + "@sideway/formula": "^3.0.1", + "@sideway/pinpoint": "^2.0.0" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "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==", + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "js2xmlparser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz", + "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==", + "requires": { + "xmlcreate": "^2.0.4" + } + }, + "jsdoc": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.2.tgz", + "integrity": "sha512-e8cIg2z62InH7azBBi3EsSEqrKx+nUtAS5bBcYTSpZFA+vhNPyhv8PTFZ0WsjOPDj04/dOLlm08EDcQJDqaGQg==", + "requires": { + "@babel/parser": "^7.20.15", + "@jsdoc/salty": "^0.2.1", + "@types/markdown-it": "^12.2.3", + "bluebird": "^3.7.2", + "catharsis": "^0.9.0", + "escape-string-regexp": "^2.0.0", + "js2xmlparser": "^4.0.2", + "klaw": "^3.0.0", + "markdown-it": "^12.3.2", + "markdown-it-anchor": "^8.4.1", + "marked": "^4.0.10", + "mkdirp": "^1.0.4", + "requizzle": "^0.2.3", + "strip-json-comments": "^3.1.0", + "underscore": "~1.13.2" + }, + "dependencies": { + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, + "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==" + } + } + }, + "jsdom": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", + "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==", + "dev": true, + "requires": { + "abab": "^2.0.6", + "acorn": "^8.8.1", + "acorn-globals": "^7.0.0", + "cssom": "^0.5.0", + "cssstyle": "^2.3.0", + "data-urls": "^3.0.2", + "decimal.js": "^10.4.2", + "domexception": "^4.0.0", + "escodegen": "^2.0.0", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.1", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.2", + "parse5": "^7.1.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.1.2", + "w3c-xmlserializer": "^4.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^2.0.0", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0", + "ws": "^8.11.0", + "xml-name-validator": "^4.0.0" + }, + "dependencies": { + "saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "requires": { + "xmlchars": "^2.2.0" + } + } + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" + }, + "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==" + }, + "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==" + }, + "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==" + }, + "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 + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true + }, + "json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==" + }, + "jsonc-eslint-parser": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/jsonc-eslint-parser/-/jsonc-eslint-parser-2.4.0.tgz", + "integrity": "sha512-WYDyuc/uFcGp6YtM2H0uKmUwieOuzeE/5YocFJLnLfclZ4inf3mRn8ZVy1s7Hxji7Jxm6Ss8gqpexD/GlKoGgg==", + "dev": true, + "requires": { + "acorn": "^8.5.0", + "eslint-visitor-keys": "^3.0.0", + "espree": "^9.0.0", + "semver": "^7.3.5" + }, + "dependencies": { + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "jsonc-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", + "dev": true + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "jsonschema": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.4.1.tgz", + "integrity": "sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ==", + "dev": true + }, + "jsx-ast-utils": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz", + "integrity": "sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==", + "dev": true, + "requires": { + "array-includes": "^3.1.5", + "object.assign": "^4.1.3" + } + }, + "jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "requires": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "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" + } + }, + "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==" + }, + "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==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "keytar": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/keytar/-/keytar-7.9.0.tgz", + "integrity": "sha512-VPD8mtVtm5JNtA2AErl6Chp06JBfy7diFQ7TQQhdpWOl6MrCRB+eRbvAZUsbGQS9kiMq0coJsy0W0vHpDCkWsQ==", + "dev": true, + "optional": true, + "requires": { + "node-addon-api": "^4.3.0", + "prebuild-install": "^7.0.1" + }, + "dependencies": { + "node-addon-api": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", + "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==", + "dev": true, + "optional": true + } + } + }, + "keyv": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.2.tgz", + "integrity": "sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g==", + "requires": { + "json-buffer": "3.0.1" + } + }, + "khroma": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/khroma/-/khroma-2.0.0.tgz", + "integrity": "sha512-2J8rDNlQWbtiNYThZRvmMv5yt44ZakX+Tz5ZIp/mN1pt4snn+m030Va5Z4v8xA0cQFDXBwO/8i42xL4QPsVk3g==" + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" + }, + "klaw": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", + "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", + "requires": { + "graceful-fs": "^4.1.9" + } + }, + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==" + }, + "klona": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", + "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==" + }, + "langium": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/langium/-/langium-3.0.0.tgz", + "integrity": "sha512-+Ez9EoiByeoTu/2BXmEaZ06iPNXM6thWJp02KfBO/raSMyCJ4jw7AkWWa+zBCTm0+Tw1Fj9FOxdqSskyN5nAwg==", + "requires": { + "chevrotain": "~11.0.3", + "chevrotain-allstar": "~0.3.0", + "vscode-languageserver": "~9.0.1", + "vscode-languageserver-textdocument": "~1.0.11", + "vscode-uri": "~3.0.8" + } + }, + "langium-cli": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/langium-cli/-/langium-cli-3.0.3.tgz", + "integrity": "sha512-g6PdhEq5IiYWK/oiySILglPvFdK6ofQdzC+U7PJmFH++bDKu0DGdxjWzDauUN5WUDyVQETWKgtYDmmbcxPzN0w==", + "dev": true, + "requires": { + "chalk": "~5.3.0", + "commander": "~11.0.0", + "fs-extra": "~11.1.1", + "jsonschema": "~1.4.1", + "langium": "~3.0.0", + "langium-railroad": "~3.0.0", + "lodash": "~4.17.21" + }, + "dependencies": { + "chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true + }, + "commander": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.0.0.tgz", + "integrity": "sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==", + "dev": true + }, + "fs-extra": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", + "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + } + } + }, + "langium-railroad": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/langium-railroad/-/langium-railroad-3.0.0.tgz", + "integrity": "sha512-GQOnQBGl5gJqzgK/4bKvJO5QhJGNnprpYH6Fghbl4FviVLHwP6yzyqiouDelLSoCadChCr2JqKaBp5HXv7CgWw==", + "dev": true, + "requires": { + "langium": "~3.0.0", + "railroad-diagrams": "~1.0.0" + } + }, + "language-subtag-registry": { + "version": "0.3.22", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", + "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==", + "dev": true + }, + "language-tags": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.6.tgz", + "integrity": "sha512-HNkaCgM8wZgE/BZACeotAAgpL9FUjEnhgF0FVQMIgH//zqTPreLYMb3rWYkYAqPoF75Jwuycp1da7uz66cfFQg==", + "dev": true, + "requires": { + "language-subtag-registry": "^0.3.20" + } + }, + "latest-version": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", + "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", + "requires": { + "package-json": "6.5.0" + } + }, + "layout-base": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz", + "integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==" + }, + "lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "requires": { + "readable-stream": "^2.0.5" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "requires": { + "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" + } + }, + "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==" + }, + "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==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "less": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/less/-/less-4.1.3.tgz", + "integrity": "sha512-w16Xk/Ta9Hhyei0Gpz9m7VS8F28nieJaL/VyShID7cYvP6IL5oHeL6p4TXSDJqZE/lNv0oJ2pGVjJsRkfwm5FA==", + "dev": true, + "requires": { + "copy-anything": "^2.0.1", + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "make-dir": "^2.1.0", + "mime": "^1.4.1", + "needle": "^3.1.0", + "parse-node-version": "^1.0.1", + "source-map": "~0.6.0", + "tslib": "^2.3.0" + }, + "dependencies": { + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "optional": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "optional": true + }, + "semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "optional": true + } + } + }, + "less-loader": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-11.1.0.tgz", + "integrity": "sha512-C+uDBV7kS7W5fJlUjq5mPBeBVhYpTIm5gB09APT9o3n/ILeaXVsiSFTbZpTJCJwQ/Crczfn3DmfQFwxYusWFug==", + "dev": true, + "requires": { + "klona": "^2.0.4" + } + }, + "leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==" + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "license-webpack-plugin": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-4.0.2.tgz", + "integrity": "sha512-771TFWFD70G1wLTC4oU2Cw4qvtmNrIw+wRvBtn+okgHl7slJVi7zfNcdmqDL72BojM30VNJ2UHylr1o77U37Jw==", + "dev": true, + "requires": { + "webpack-sources": "^3.0.0" + } + }, + "lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "requires": { + "immediate": "~3.0.5" + } + }, + "lilconfig": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", + "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==" + }, + "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==" + }, + "linkify-it": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", + "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", + "requires": { + "uc.micro": "^1.0.1" + } + }, + "listenercount": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", + "integrity": "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==" + }, + "loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==" + }, + "loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "local-pkg": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.0.tgz", + "integrity": "sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==", + "dev": true, + "requires": { + "mlly": "^1.4.2", + "pkg-types": "^1.0.3" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "requires": { + "p-locate": "^4.1.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, + "lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "dev": true + }, + "lodash.curry": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.curry/-/lodash.curry-4.1.1.tgz", + "integrity": "sha512-/u14pXGviLaweY5JI0IUzgzF2J6Ne8INyzAZjImcryjgkZ+ebruBxy2/JaOOkTqScddcYtakjhSaeemV8lR0tA==" + }, + "lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" + }, + "lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" + }, + "lodash.difference": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", + "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==" + }, + "lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==" + }, + "lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==" + }, + "lodash.flow": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/lodash.flow/-/lodash.flow-3.5.0.tgz", + "integrity": "sha512-ff3BX/tSioo+XojX4MOsOMhJw0nZoUEF011LX8g8d3gvjVbxd89cCio4BCXronjxcTUIJUoqKEUA+n4CqvvRPw==" + }, + "lodash.groupby": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.groupby/-/lodash.groupby-4.6.0.tgz", + "integrity": "sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==" + }, + "lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" + }, + "lodash.isfunction": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz", + "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==" + }, + "lodash.isnil": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/lodash.isnil/-/lodash.isnil-4.0.0.tgz", + "integrity": "sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng==" + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "lodash.isundefined": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz", + "integrity": "sha512-MXB1is3s899/cD8jheYYE2V9qTHwKvt+npCwpD+1Sxm3Q3cECXCiYHjeHWXNwr6Q0SOBPrYUDxendrO6goVTEA==" + }, + "lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==" + }, + "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 + }, + "lodash.union": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", + "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==" + }, + "lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==" + }, + "log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + } + }, + "long": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.1.tgz", + "integrity": "sha512-GKSNGeNAtw8IryjjkhZxuKB3JzlcLTwjtiQCHKvqQet81I93kXslhDQruGI/QsddO83mcDToBVy7GqGS/zYf/A==" + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "dev": true, + "requires": { + "get-func-name": "^2.0.1" + } + }, + "lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "requires": { + "tslib": "^2.0.3" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "magic-string": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", + "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", + "dev": true, + "requires": { + "sourcemap-codec": "^1.4.8" + } + }, + "magicast": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.4.tgz", + "integrity": "sha512-TyDF/Pn36bBji9rWKHlZe+PZb6Mx5V8IHCSxk7X4aljM4e/vyDvZZYwHewdVaqiA0nb3ghfHU/6AUpDxWoER2Q==", + "dev": true, + "requires": { + "@babel/parser": "^7.24.4", + "@babel/types": "^7.24.0", + "source-map-js": "^1.2.0" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "make-fetch-happen": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", + "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", + "optional": true, + "requires": { + "agentkeepalive": "^4.1.3", + "cacache": "^15.2.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.2", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^6.0.0", + "ssri": "^8.0.0" + }, + "dependencies": { + "@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "optional": true + }, + "http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "optional": true, + "requires": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + } + } + } + }, + "makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "requires": { + "tmpl": "1.0.5" + } + }, + "markdown-escapes": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/markdown-escapes/-/markdown-escapes-1.0.4.tgz", + "integrity": "sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg==" + }, + "markdown-it": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", + "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", + "requires": { + "argparse": "^2.0.1", + "entities": "~2.1.0", + "linkify-it": "^3.0.1", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + }, + "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "entities": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", + "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==" + } + } + }, + "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==" + }, + "marked": { + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.2.12.tgz", + "integrity": "sha512-yr8hSKa3Fv4D3jdZmtMMPghgVt6TWbk86WQaWhDloQjRSQhMMYCAro7jP7VDJrjjdV8pxVxMssXS8B8Y5DZ5aw==" + }, + "mdast-squeeze-paragraphs": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-squeeze-paragraphs/-/mdast-squeeze-paragraphs-4.0.0.tgz", + "integrity": "sha512-zxdPn69hkQ1rm4J+2Cs2j6wDEv7O17TfXTJ33tl/+JPIoEmtV9t2ZzBM5LPHE8QlHsmVD8t3vPKCyY3oH+H8MQ==", + "requires": { + "unist-util-remove": "^2.0.0" + } + }, + "mdast-util-definitions": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-4.0.0.tgz", + "integrity": "sha512-k8AJ6aNnUkB7IE+5azR9h81O5EQ/cTDXtWdMq9Kk5KcEW/8ritU5CeLg/9HhOC++nALHBlaogJ5jz0Ybk3kPMQ==", + "requires": { + "unist-util-visit": "^2.0.0" + } + }, + "mdast-util-to-hast": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-10.0.1.tgz", + "integrity": "sha512-BW3LM9SEMnjf4HXXVApZMt8gLQWVNXc3jryK0nJu/rOXPOnlkUjmdkDlmxMirpbU9ILncGFIwLH/ubnWBbcdgA==", + "requires": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "mdast-util-definitions": "^4.0.0", + "mdurl": "^1.0.0", + "unist-builder": "^2.0.0", + "unist-util-generated": "^1.0.0", + "unist-util-position": "^3.0.0", + "unist-util-visit": "^2.0.0" + } + }, + "mdast-util-to-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz", + "integrity": "sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==" + }, + "mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" + }, + "mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==" + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" + }, + "memfs": { + "version": "3.4.12", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.12.tgz", + "integrity": "sha512-BcjuQn6vfqP+k100e0E9m61Hyqa//Brp+I3f0OBmN0ATHlFA8vx3Lt8z57R3u2bPqe3WGDBC+nF72fTH7isyEw==", + "requires": { + "fs-monkey": "^1.0.3" + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" + }, + "mermaid": { + "version": "9.4.3", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-9.4.3.tgz", + "integrity": "sha512-TLkQEtqhRSuEHSE34lh5bCa94KATCyluAXmFnNI2PRZwOpXFeqiJWwZl+d2CcemE1RS6QbbueSSq9QIg8Uxcyw==", + "requires": { + "@braintree/sanitize-url": "^6.0.0", + "cytoscape": "^3.23.0", + "cytoscape-cose-bilkent": "^4.1.0", + "cytoscape-fcose": "^2.1.0", + "d3": "^7.4.0", + "dagre-d3-es": "7.0.9", + "dayjs": "^1.11.7", + "dompurify": "2.4.3", + "elkjs": "^0.8.2", + "khroma": "^2.0.0", + "lodash-es": "^4.17.21", + "non-layered-tidy-tree-layout": "^2.0.2", + "stylis": "^4.1.2", + "ts-dedent": "^2.2.0", + "uuid": "^9.0.0", + "web-worker": "^1.2.0" + }, + "dependencies": { + "uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==" + } + } + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" + }, + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "requires": { + "mime-db": "1.52.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" + }, + "min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true + }, + "mini-css-extract-plugin": { + "version": "2.4.7", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.4.7.tgz", + "integrity": "sha512-euWmddf0sk9Nv1O0gfeeUAvAkoSlWncNLF77C0TP2+WoPvy8mAHKOzMajcCz2dzvyt3CNgxb1obIEVFIRxaipg==", + "dev": true, + "requires": { + "schema-utils": "^4.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + } + } + } + }, + "mini-svg-data-uri": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz", + "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==", + "dev": true + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "minimatch": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.5.tgz", + "integrity": "sha512-tUpxzX0VAzJHjLu0xUfFv1gwVp9ba3IOuRAVH2EGuRW8a5emA2FlACLqiT/lDVtS1W+TGNwqz3sWaNyLgDJWuw==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==" + }, + "minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "requires": { + "yallist": "^4.0.0" + } + }, + "minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "optional": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-fetch": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", + "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", + "optional": true, + "requires": { + "encoding": "^0.1.12", + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" + } + }, + "minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "optional": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "optional": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "optional": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + } + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + }, + "mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true, + "optional": true + }, + "mlly": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.0.tgz", + "integrity": "sha512-U9SDaXGEREBYQgfejV97coK0UL1r+qnF2SyO9A3qcI8MzKnsIFKHNVEkrDyNncQTKQQumsasmeq84eNMdBfsNQ==", + "dev": true, + "requires": { + "acorn": "^8.11.3", + "pathe": "^1.1.2", + "pkg-types": "^1.1.0", + "ufo": "^1.5.3" + } + }, + "monaco-editor": { + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.34.1.tgz", + "integrity": "sha512-FKc80TyiMaruhJKKPz5SpJPIjL+dflGvz4CpuThaPMc94AyN7SeC9HQ8hrvaxX7EyHdJcUY5i4D0gNyJj1vSZQ==" + }, + "monaco-languageclient": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/monaco-languageclient/-/monaco-languageclient-4.0.3.tgz", + "integrity": "sha512-1mGIUb5PFRknITBhNxgH0SnQy1/jntt9oo0cQpOl3HdhYEL/CYK2UrsZZX7Udqmz1PXKyRIzQ3tZ7dJn4mzWtA==", + "requires": { + "vscode": "npm:@codingame/monaco-vscode-api@1.69.13", + "vscode-jsonrpc": "8.0.2", + "vscode-languageclient": "8.0.2" + }, + "dependencies": { + "semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "vscode-languageclient": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-8.0.2.tgz", + "integrity": "sha512-lHlthJtphG9gibGb/y72CKqQUxwPsMXijJVpHEC2bvbFqxmkj9LwQ3aGU9dwjBLqsX1S4KjShYppLvg1UJDF/Q==", + "requires": { + "minimatch": "^3.0.4", + "semver": "^7.3.5", + "vscode-languageserver-protocol": "3.17.2" + } + } + } + }, + "mrmime": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz", + "integrity": "sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==" + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "multicast-dns": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", + "requires": { + "dns-packet": "^5.2.2", + "thunky": "^1.0.2" + } + }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==" + }, + "napi-build-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", + "dev": true, + "optional": true + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true + }, + "needle": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/needle/-/needle-3.3.1.tgz", + "integrity": "sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==", + "dev": true, + "optional": true, + "requires": { + "iconv-lite": "^0.6.3", + "sax": "^1.2.4" + } + }, + "negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" + }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + }, + "no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "requires": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "nock": { + "version": "13.3.1", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.3.1.tgz", + "integrity": "sha512-vHnopocZuI93p2ccivFyGuUfzjq2fxNyNurp7816mlT5V5HF4SzXu8lvLrVzBbNqzs+ODooZ6OksuSUNM7Njkw==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "json-stringify-safe": "^5.0.1", + "lodash": "^4.17.21", + "propagate": "^2.0.0" + } + }, + "node-abi": { + "version": "3.33.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.33.0.tgz", + "integrity": "sha512-7GGVawqyHF4pfd0YFybhv/eM9JwTtPqx0mAanQ146O3FlSh3pA24zf9IRQTOsfTSqXTNzPSP5iagAJ94jjuVog==", + "dev": true, + "optional": true, + "requires": { + "semver": "^7.3.5" + }, + "dependencies": { + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "optional": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "node-abort-controller": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", + "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==", + "dev": true + }, + "node-emoji": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", + "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", + "requires": { + "lodash": "^4.17.21" + } + }, + "node-fetch": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz", + "integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==", + "requires": { + "whatwg-url": "^5.0.0" + }, + "dependencies": { + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + } + } + }, + "node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==" + }, + "node-gyp": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", + "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", + "optional": true, + "requires": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^9.1.0", + "nopt": "^5.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "dependencies": { + "are-we-there-yet": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", + "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", + "optional": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + } + }, + "gauge": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "optional": true, + "requires": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + } + }, + "npmlog": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", + "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "optional": true, + "requires": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + } + }, + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "optional": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true + }, + "node-machine-id": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/node-machine-id/-/node-machine-id-1.1.12.tgz", + "integrity": "sha512-QNABxbrPa3qEIfrE6GOJ7BYIuignnJw7iQ2YPbc3Nla1HzRJjXzZOiikfF8m7eAMfichLt3M4VgLOetqgDmgGQ==", + "dev": true + }, + "node-releases": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==" + }, + "nodejs-polars": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/nodejs-polars/-/nodejs-polars-0.11.0.tgz", + "integrity": "sha512-rLhYkhcMCGrRXSUOVt+wskNiqaSapEWpo7hPlhQvEFkjcn4NPO/0RPHswGezOdIgQ3h778jpNkIXQjxdD5bXdA==", + "requires": { + "nodejs-polars-android-arm64": "0.11.0", + "nodejs-polars-darwin-arm64": "0.11.0", + "nodejs-polars-darwin-x64": "0.11.0", + "nodejs-polars-linux-arm64-gnu": "0.11.0", + "nodejs-polars-linux-arm64-musl": "0.11.0", + "nodejs-polars-linux-x64-gnu": "0.11.0", + "nodejs-polars-linux-x64-musl": "0.11.0", + "nodejs-polars-win32-x64-msvc": "0.11.0" + } + }, + "nodejs-polars-android-arm64": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/nodejs-polars-android-arm64/-/nodejs-polars-android-arm64-0.11.0.tgz", + "integrity": "sha512-AuwmFvhrOELkfqmhV2ZArxV66XLlHNP/1vZqUgbPqG4nmvKi9J4RGRnKXNW+BjDgNzV/PF2OoJekEwe/Nh+whA==", + "optional": true + }, + "nodejs-polars-darwin-arm64": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/nodejs-polars-darwin-arm64/-/nodejs-polars-darwin-arm64-0.11.0.tgz", + "integrity": "sha512-4VuRbWV0s5KlP8Zdw0GY2SGRXS25Ih/3qo0F3VVObDDxu60sU0m32z3fGMziphQw0Dl7Ldk1cloYyu72gnfKCQ==", + "optional": true + }, + "nodejs-polars-darwin-x64": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/nodejs-polars-darwin-x64/-/nodejs-polars-darwin-x64-0.11.0.tgz", + "integrity": "sha512-SAjq47Mk1cSBpf0czTxSbHMM5vTCwKdnsSFQTtUYyjqN+22l8h5oTDPOppMQ6coq0DxLugnSvmrIOeZ0HY213A==", + "optional": true + }, + "nodejs-polars-linux-arm64-gnu": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/nodejs-polars-linux-arm64-gnu/-/nodejs-polars-linux-arm64-gnu-0.11.0.tgz", + "integrity": "sha512-BH9ePSHYDlG5ggg/YM/kvLuv8TbctRAUUEhCnpgaJeBPnrtRJC+hzbWKpTcGc+pOwvunfGS+/FG9BzLtFb9POg==", + "optional": true + }, + "nodejs-polars-linux-arm64-musl": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/nodejs-polars-linux-arm64-musl/-/nodejs-polars-linux-arm64-musl-0.11.0.tgz", + "integrity": "sha512-IYZ/xIACiedX4gjklyKzD1/gOOOSvCeINEB3Vc6Ene0cQI52PZtJNwfIo44ofM3Jqx0YDrLW88y5Myu+NdwmOw==", + "optional": true + }, + "nodejs-polars-linux-x64-gnu": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/nodejs-polars-linux-x64-gnu/-/nodejs-polars-linux-x64-gnu-0.11.0.tgz", + "integrity": "sha512-SnJdhVAGPAxQ5ZB3QVacCF6SnzWI7/2qp1X4AEvaVFoAaZp3ahdE+1K9qYS3EZZM5y1DdVbok1cJzQs8UlM9VA==", + "optional": true + }, + "nodejs-polars-linux-x64-musl": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/nodejs-polars-linux-x64-musl/-/nodejs-polars-linux-x64-musl-0.11.0.tgz", + "integrity": "sha512-ug8f34akvrrHDU8UvObU45VswwVJYN85AUF+K5vZGh7zGUoZlwqG4PfWr2vKjSz41XrfgqkeMwoFZlTCrrMNoA==", + "optional": true + }, + "nodejs-polars-win32-x64-msvc": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/nodejs-polars-win32-x64-msvc/-/nodejs-polars-win32-x64-msvc-0.11.0.tgz", + "integrity": "sha512-MF3rVxabCAy6GJ+ohaeCsGUn3pjOlHwjj0iCgjHxdJDvmXpNl2TTFqGMijicDqsRbv/TKt90o6QUrBZl94MQJQ==", + "optional": true + }, + "non-layered-tidy-tree-layout": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/non-layered-tidy-tree-layout/-/non-layered-tidy-tree-layout-2.0.2.tgz", + "integrity": "sha512-gkXMxRzUH+PB0ax9dUN0yYF0S25BqeAYqhgMaLUFmpXLEk7Fcu8f4emJuOAY0V8kjDICxROIKsTAKsV/v355xw==" + }, + "nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "requires": { + "abbrev": "1" + } + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true + } + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + }, + "normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==" + }, + "npm-package-arg": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-11.0.1.tgz", + "integrity": "sha512-M7s1BD4NxdAvBKUPqqRW957Xwcl/4Zvo8Aj+ANrzvIPzGJZElrH7Z//rSaec2ORcND6FHHLnZeY8qgTpXDMFQQ==", + "dev": true, + "requires": { + "hosted-git-info": "^7.0.0", + "proc-log": "^3.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^5.0.0" + }, + "dependencies": { + "hosted-git-info": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.1.tgz", + "integrity": "sha512-+K84LB1DYwMHoHSgaOY/Jfhw3ucPmSET5v98Ke/HdNSw4a0UktWzyW1mjhjpuxxTqOOsfWT/7iVshHmVZ4IpOA==", + "dev": true, + "requires": { + "lru-cache": "^10.0.1" + }, + "dependencies": { + "lru-cache": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", + "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", + "dev": true + } + } + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "requires": { + "path-key": "^3.0.0" + } + }, + "npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "requires": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, + "nprogress": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz", + "integrity": "sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==" + }, + "nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "requires": { + "boolbase": "^1.0.0" + } + }, + "nwsapi": { + "version": "2.2.9", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.9.tgz", + "integrity": "sha512-2f3F0SEEer8bBu0dsNCFF50N0cTThV1nWFYcEYFZttdW0lDAoybv9cQoK7X7/68Z89S7FoRrVjP1LPX4XRf9vg==", + "dev": true + }, + "nx": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/nx/-/nx-18.0.1.tgz", + "integrity": "sha512-AtcM7JmBC82O17WMxuu9JJxEKTcsMII1AMgxCeiCWcW22wHd3EhIn5Hg1iSFv9ftkSSd8YgHeqTciRbdTqbxpA==", + "dev": true, + "requires": { + "@nrwl/tao": "18.0.1", + "@nx/nx-darwin-arm64": "18.0.1", + "@nx/nx-darwin-x64": "18.0.1", + "@nx/nx-freebsd-x64": "18.0.1", + "@nx/nx-linux-arm-gnueabihf": "18.0.1", + "@nx/nx-linux-arm64-gnu": "18.0.1", + "@nx/nx-linux-arm64-musl": "18.0.1", + "@nx/nx-linux-x64-gnu": "18.0.1", + "@nx/nx-linux-x64-musl": "18.0.1", + "@nx/nx-win32-arm64-msvc": "18.0.1", + "@nx/nx-win32-x64-msvc": "18.0.1", + "@yarnpkg/lockfile": "^1.1.0", + "@yarnpkg/parsers": "3.0.0-rc.46", + "@zkochan/js-yaml": "0.0.6", + "axios": "^1.6.0", + "chalk": "^4.1.0", + "cli-cursor": "3.1.0", + "cli-spinners": "2.6.1", + "cliui": "^8.0.1", + "dotenv": "~16.3.1", + "dotenv-expand": "~10.0.0", + "enquirer": "~2.3.6", + "figures": "3.2.0", + "flat": "^5.0.2", + "fs-extra": "^11.1.0", + "ignore": "^5.0.4", + "jest-diff": "^29.4.1", + "js-yaml": "4.1.0", + "jsonc-parser": "3.2.0", + "lines-and-columns": "~2.0.3", + "minimatch": "9.0.3", + "node-machine-id": "1.1.12", + "npm-run-path": "^4.0.1", + "open": "^8.4.0", + "ora": "5.3.0", + "semver": "^7.5.3", + "string-width": "^4.2.3", + "strong-log-transformer": "^2.1.0", + "tar-stream": "~2.2.0", + "tmp": "~0.2.1", + "tsconfig-paths": "^4.1.2", + "tslib": "^2.3.0", + "yargs": "^17.6.2", + "yargs-parser": "21.1.1" + }, + "dependencies": { + "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 + }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "lines-and-columns": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-2.0.4.tgz", + "integrity": "sha512-wM1+Z03eypVAVUCE7QdSqpVIvelbOakn1M0bPDoA4SGWPx3sNDVUiMo3L6To6WWGClB7VyXnhQ4Sn7gxiJbE6A==", + "dev": true + }, + "minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true + }, + "tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "requires": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + } + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" + }, + "object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==" + }, + "object-is": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + }, + "object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + } + }, + "object.entries": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.6.tgz", + "integrity": "sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + } + }, + "object.fromentries": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.6.tgz", + "integrity": "sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + } + }, + "object.hasown": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.2.tgz", + "integrity": "sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==", + "dev": true, + "requires": { + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + } + }, + "object.values": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", + "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + } + }, + "obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" + }, + "on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "open": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "requires": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + } + }, + "opener": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==" + }, + "optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "requires": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + } + }, + "ora": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.3.0.tgz", + "integrity": "sha512-zAKMgGXUim0Jyd6CXK9lraBnD3H5yPGBPPOkC23a2BG6hsm4Zu6OQSjQuEtV0BHDf4aKHcUFvJiGRrFuW3MG8g==", + "dev": true, + "requires": { + "bl": "^4.0.3", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "log-symbols": "^4.0.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + } + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "dev": true + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "requires": { + "p-limit": "^2.2.0" + }, + "dependencies": { + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "requires": { + "p-try": "^2.0.0" + } + } + } + }, + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "p-queue": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", + "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", + "dev": true, + "requires": { + "eventemitter3": "^4.0.4", + "p-timeout": "^3.2.0" + } + }, + "p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "requires": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + } + }, + "p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "dev": true, + "requires": { + "p-finally": "^1.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + }, + "package-json": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", + "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", + "requires": { + "got": "^11.8.5", + "registry-auth-token": "^4.0.0", + "registry-url": "^5.0.0", + "semver": "^6.2.0" + }, + "dependencies": { + "@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==" + }, + "@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "requires": { + "defer-to-connect": "^2.0.0" + } + }, + "cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==" + }, + "cacheable-request": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", + "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + } + }, + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "requires": { + "pump": "^3.0.0" + } + }, + "got": { + "version": "11.8.6", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", + "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", + "requires": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + } + }, + "http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "requires": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" + }, + "normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==" + }, + "p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==" + }, + "responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "requires": { + "lowercase-keys": "^2.0.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "packet-reader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", + "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" + }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" + }, + "param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "requires": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "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==", + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-entities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", + "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", + "requires": { + "character-entities": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "character-reference-invalid": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-hexadecimal": "^1.0.0" + } + }, + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "parse-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", + "dev": true + }, + "parse-numeric-range": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/parse-numeric-range/-/parse-numeric-range-1.3.0.tgz", + "integrity": "sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ==" + }, + "parse-semver": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/parse-semver/-/parse-semver-1.1.1.tgz", + "integrity": "sha512-Eg1OuNntBMH0ojvEKSrvDSnwLmvVuUOSdylH/pSCPNMIspLlweJyIWXCE+k/5hm3cj/EBUYwmWkjhBALNP4LXQ==", + "dev": true, + "requires": { + "semver": "^5.1.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "requires": { + "entities": "^4.4.0" + }, + "dependencies": { + "entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==" + } + } + }, + "parse5-htmlparser2-tree-adapter": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", + "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", + "requires": { + "domhandler": "^5.0.2", + "parse5": "^7.0.0" + }, + "dependencies": { + "domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "requires": { + "domelementtype": "^2.3.0" + } + } + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "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==" + }, + "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==" + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==" + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + }, + "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==" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" + }, + "pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true + }, + "pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true + }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true + }, + "pg": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.8.0.tgz", + "integrity": "sha512-UXYN0ziKj+AeNNP7VDMwrehpACThH7LUl/p8TDFpEUuSejCUIwGSfxpHsPvtM6/WXFy6SU4E5RG4IJV/TZAGjw==", + "requires": { + "buffer-writer": "2.0.0", + "packet-reader": "1.0.0", + "pg-connection-string": "^2.5.0", + "pg-pool": "^3.5.2", + "pg-protocol": "^1.5.0", + "pg-types": "^2.1.0", + "pgpass": "1.x" + } + }, + "pg-connection-string": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz", + "integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==" + }, + "pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==" + }, + "pg-pool": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.5.2.tgz", + "integrity": "sha512-His3Fh17Z4eg7oANLob6ZvH8xIVen3phEZh2QuyrIl4dQSDVEabNducv6ysROKpDNPSD+12tONZVWfSgMvDD9w==" + }, + "pg-protocol": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.5.0.tgz", + "integrity": "sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ==" + }, + "pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "requires": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + } + }, + "pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "requires": { + "split2": "^4.1.0" + } + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" + }, + "pify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-5.0.0.tgz", + "integrity": "sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==", + "dev": true + }, + "pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "requires": { + "find-up": "^4.0.0" + } + }, + "pkg-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.1.0.tgz", + "integrity": "sha512-/RpmvKdxKf8uILTtoOhAgf30wYbP2Qw+L9p3Rvshx1JZVX+XQNZQFjlbmGHEGIm4CkVPlSn+NXmIM8+9oWQaSA==", + "dev": true, + "requires": { + "confbox": "^0.1.7", + "mlly": "^1.6.1", + "pathe": "^1.1.2" + } + }, + "pkg-up": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", + "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "requires": { + "find-up": "^3.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "requires": { + "p-try": "^2.0.0" + } + }, + "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==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==" + } + } + }, + "pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true + }, + "portfinder": { + "version": "1.0.32", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.32.tgz", + "integrity": "sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg==", + "dev": true, + "requires": { + "async": "^2.6.4", + "debug": "^3.2.7", + "mkdirp": "^0.5.6" + }, + "dependencies": { + "async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dev": true, + "requires": { + "lodash": "^4.17.14" + } + }, + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "requires": { + "minimist": "^1.2.6" + } + } + } + }, + "postcss": { + "version": "8.4.33", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz", + "integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==", + "requires": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + } + }, + "postcss-calc": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz", + "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==", + "requires": { + "postcss-selector-parser": "^6.0.9", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-colormin": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.1.tgz", + "integrity": "sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ==", + "requires": { + "browserslist": "^4.21.4", + "caniuse-api": "^3.0.0", + "colord": "^2.9.1", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-convert-values": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.3.tgz", + "integrity": "sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==", + "requires": { + "browserslist": "^4.21.4", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-discard-comments": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz", + "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==" + }, + "postcss-discard-duplicates": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz", + "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==" + }, + "postcss-discard-empty": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz", + "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==" + }, + "postcss-discard-overridden": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz", + "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==" + }, + "postcss-discard-unused": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-5.1.0.tgz", + "integrity": "sha512-KwLWymI9hbwXmJa0dkrzpRbSJEh0vVUd7r8t0yOGPcfKzyJJxFM8kLyC5Ev9avji6nY95pOp1W6HqIrfT+0VGw==", + "requires": { + "postcss-selector-parser": "^6.0.5" + } + }, + "postcss-import": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz", + "integrity": "sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + } + }, + "postcss-load-config": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", + "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", + "dev": true, + "requires": { + "lilconfig": "^2.0.5", + "yaml": "^1.10.2" + } + }, + "postcss-loader": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz", + "integrity": "sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==", + "dev": true, + "requires": { + "cosmiconfig": "^7.0.0", + "klona": "^2.0.5", + "semver": "^7.3.5" + }, + "dependencies": { + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "postcss-merge-idents": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-5.1.1.tgz", + "integrity": "sha512-pCijL1TREiCoog5nQp7wUe+TUonA2tC2sQ54UGeMmryK3UFGIYKqDyjnqd6RcuI4znFn9hWSLNN8xKE/vWcUQw==", + "requires": { + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-merge-longhand": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.7.tgz", + "integrity": "sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==", + "requires": { + "postcss-value-parser": "^4.2.0", + "stylehacks": "^5.1.1" + } + }, + "postcss-merge-rules": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.4.tgz", + "integrity": "sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g==", + "requires": { + "browserslist": "^4.21.4", + "caniuse-api": "^3.0.0", + "cssnano-utils": "^3.1.0", + "postcss-selector-parser": "^6.0.5" + } + }, + "postcss-minify-font-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz", + "integrity": "sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-minify-gradients": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz", + "integrity": "sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==", + "requires": { + "colord": "^2.9.1", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-minify-params": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.4.tgz", + "integrity": "sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==", + "requires": { + "browserslist": "^4.21.4", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-minify-selectors": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz", + "integrity": "sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==", + "requires": { + "postcss-selector-parser": "^6.0.5" + } + }, + "postcss-modules": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/postcss-modules/-/postcss-modules-4.3.1.tgz", + "integrity": "sha512-ItUhSUxBBdNamkT3KzIZwYNNRFKmkJrofvC2nWab3CPKhYBQ1f27XXh1PAPE27Psx58jeelPsxWB/+og+KEH0Q==", + "dev": true, + "requires": { + "generic-names": "^4.0.0", + "icss-replace-symbols": "^1.1.0", + "lodash.camelcase": "^4.3.0", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "string-hash": "^1.1.1" + } + }, + "postcss-modules-extract-imports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==" + }, + "postcss-modules-local-by-default": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", + "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", + "requires": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-modules-scope": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", + "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "requires": { + "postcss-selector-parser": "^6.0.4" + } + }, + "postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "requires": { + "icss-utils": "^5.0.0" + } + }, + "postcss-normalize-charset": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz", + "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==" + }, + "postcss-normalize-display-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz", + "integrity": "sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-positions": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz", + "integrity": "sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-repeat-style": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz", + "integrity": "sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-string": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz", + "integrity": "sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-timing-functions": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz", + "integrity": "sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-unicode": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.1.tgz", + "integrity": "sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==", + "requires": { + "browserslist": "^4.21.4", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz", + "integrity": "sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==", + "requires": { + "normalize-url": "^6.0.1", + "postcss-value-parser": "^4.2.0" + }, + "dependencies": { + "normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==" + } + } + }, + "postcss-normalize-whitespace": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz", + "integrity": "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-ordered-values": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz", + "integrity": "sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==", + "requires": { + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-reduce-idents": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-5.2.0.tgz", + "integrity": "sha512-BTrLjICoSB6gxbc58D5mdBK8OhXRDqud/zodYfdSi52qvDHdMwk+9kB9xsM8yJThH/sZU5A6QVSmMmaN001gIg==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-reduce-initial": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.2.tgz", + "integrity": "sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg==", + "requires": { + "browserslist": "^4.21.4", + "caniuse-api": "^3.0.0" + } + }, + "postcss-reduce-transforms": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz", + "integrity": "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-selector-parser": { + "version": "6.0.15", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz", + "integrity": "sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==", + "requires": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + } + }, + "postcss-sort-media-queries": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/postcss-sort-media-queries/-/postcss-sort-media-queries-4.4.1.tgz", + "integrity": "sha512-QDESFzDDGKgpiIh4GYXsSy6sek2yAwQx1JASl5AxBtU1Lq2JfKBljIPNdil989NcSKRQX1ToiaKphImtBuhXWw==", + "requires": { + "sort-css-media-queries": "2.1.0" + } + }, + "postcss-svgo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.1.0.tgz", + "integrity": "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==", + "requires": { + "postcss-value-parser": "^4.2.0", + "svgo": "^2.7.0" + } + }, + "postcss-unique-selectors": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz", + "integrity": "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==", + "requires": { + "postcss-selector-parser": "^6.0.5" + } + }, + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "postcss-zindex": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-5.1.0.tgz", + "integrity": "sha512-fgFMf0OtVSBR1va1JNHYgMxYk73yhn/qb4uQDq1DLGYolz8gHCyr/sesEuGUaYs58E3ZJRcpoGuPVoB7Meiq9A==" + }, + "postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==" + }, + "postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==" + }, + "postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==" + }, + "postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "requires": { + "xtend": "^4.0.0" + } + }, + "prebuild-install": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", + "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==", + "dev": true, + "optional": true, + "requires": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + } + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "prettier": { + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.7.tgz", + "integrity": "sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw==", + "dev": true + }, + "prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "requires": { + "fast-diff": "^1.1.2" + } + }, + "pretty-error": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", + "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", + "requires": { + "lodash": "^4.17.20", + "renderkid": "^3.0.0" + } + }, + "pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + } + } + }, + "pretty-time": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pretty-time/-/pretty-time-1.1.0.tgz", + "integrity": "sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==" + }, + "prism-react-renderer": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-1.3.5.tgz", + "integrity": "sha512-IJ+MSwBWKG+SM3b2SUfdrhC+gu01QkV2KmRQgREThBfSQRoufqRfxfHUxpG1WcaFjP+kojcFyO9Qqtpgt3qLCg==" + }, + "prismjs": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", + "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==" + }, + "proc-log": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-3.0.0.tgz", + "integrity": "sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==", + "dev": true + }, + "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==" + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "requires": { + "asap": "~2.0.3" + } + }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "optional": true + }, + "promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "optional": true, + "requires": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "dependencies": { + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "optional": true + } + } + }, + "promise.series": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/promise.series/-/promise.series-0.2.0.tgz", + "integrity": "sha512-VWQJyU2bcDTgZw8kpfBpB/ejZASlCrzwz5f2hjb/zlujOEB4oeiAhHygAWq8ubsX2GVkD4kCU5V2dwOTaCY5EQ==", + "dev": true + }, + "prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "requires": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + } + }, + "prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + }, + "dependencies": { + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + } + } + }, + "propagate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", + "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", + "dev": true + }, + "property-information": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz", + "integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==", + "requires": { + "xtend": "^4.0.0" + } + }, + "protobufjs": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.2.tgz", + "integrity": "sha512-++PrQIjrom+bFDPpfmqXfAGSQs40116JRrqqyf53dymUMvvb5d/LMRyicRoF1AUKoXVS1/IgJXlEgcpr4gTF3Q==", + "requires": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + } + }, + "protobufjs-cli": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/protobufjs-cli/-/protobufjs-cli-1.1.1.tgz", + "integrity": "sha512-VPWMgIcRNyQwWUv8OLPyGQ/0lQY/QTQAVN5fh+XzfDwsVw1FZ2L3DM/bcBf8WPiRz2tNpaov9lPZfNcmNo6LXA==", + "requires": { + "chalk": "^4.0.0", + "escodegen": "^1.13.0", + "espree": "^9.0.0", + "estraverse": "^5.1.0", + "glob": "^8.0.0", + "jsdoc": "^4.0.0", + "minimist": "^1.2.0", + "semver": "^7.1.2", + "tmp": "^0.2.1", + "uglify-js": "^3.7.7" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "requires": { + "balanced-match": "^1.0.0" + } + }, + "escodegen": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "requires": { + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + }, + "dependencies": { + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" + } + } + }, + "glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + } + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==" + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "requires": { + "prelude-ls": "~1.1.2" + } + } + } + }, + "proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "dependencies": { + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + } + } + }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", + "dev": true, + "optional": true + }, + "psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", + "dev": true + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "pupa": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", + "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", + "requires": { + "escape-goat": "^2.0.0" + } + }, + "pure-color": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/pure-color/-/pure-color-1.3.0.tgz", + "integrity": "sha512-QFADYnsVoBMw1srW7OVKEYjG+MbIa49s54w1MA1EDY6r2r/sTcKKYqRX1f4GYvnXP7eN/Pe9HFcX+hwzmrXRHA==" + }, + "pure-rand": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.4.tgz", + "integrity": "sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==", + "dev": true + }, + "qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "requires": { + "side-channel": "^1.0.4" + } + }, + "querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true + }, + "queue": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", + "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", + "requires": { + "inherits": "~2.0.3" + } + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" + }, + "quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==" + }, + "railroad-diagrams": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz", + "integrity": "sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A==", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "requires": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "dependencies": { + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + } + } + }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==" + } + } + }, + "react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "requires": { + "loose-envify": "^1.1.0" + } + }, + "react-base16-styling": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/react-base16-styling/-/react-base16-styling-0.6.0.tgz", + "integrity": "sha512-yvh/7CArceR/jNATXOKDlvTnPKPmGZz7zsenQ3jUwLzHkNUR0CvY3yGYJbWJ/nnxsL8Sgmt5cO3/SILVuPO6TQ==", + "requires": { + "base16": "^1.0.0", + "lodash.curry": "^4.0.1", + "lodash.flow": "^3.3.0", + "pure-color": "^1.2.0" + } + }, + "react-dev-utils": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", + "integrity": "sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==", + "requires": { + "@babel/code-frame": "^7.16.0", + "address": "^1.1.2", + "browserslist": "^4.18.1", + "chalk": "^4.1.2", + "cross-spawn": "^7.0.3", + "detect-port-alt": "^1.1.6", + "escape-string-regexp": "^4.0.0", + "filesize": "^8.0.6", + "find-up": "^5.0.0", + "fork-ts-checker-webpack-plugin": "^6.5.0", + "global-modules": "^2.0.0", + "globby": "^11.0.4", + "gzip-size": "^6.0.0", + "immer": "^9.0.7", + "is-root": "^2.1.0", + "loader-utils": "^3.2.0", + "open": "^8.4.0", + "pkg-up": "^3.1.0", + "prompts": "^2.4.2", + "react-error-overlay": "^6.0.11", + "recursive-readdir": "^2.2.2", + "shell-quote": "^1.7.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "dependencies": { + "cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" + } + }, + "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==" + }, + "fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "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==", + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "fork-ts-checker-webpack-plugin": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.2.tgz", + "integrity": "sha512-m5cUmF30xkZ7h4tWUgTAcEaKmUW7tfyUyTqNNOz7OxWJ0v1VWKTcOvH8FWHUwSjlW/356Ijc9vi3XfcPstpQKA==", + "requires": { + "@babel/code-frame": "^7.8.3", + "@types/json-schema": "^7.0.5", + "chalk": "^4.1.0", + "chokidar": "^3.4.2", + "cosmiconfig": "^6.0.0", + "deepmerge": "^4.2.2", + "fs-extra": "^9.0.0", + "glob": "^7.1.6", + "memfs": "^3.1.2", + "minimatch": "^3.0.4", + "schema-utils": "2.7.0", + "semver": "^7.3.2", + "tapable": "^1.0.0" + } + }, + "fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, + "loader-utils": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz", + "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==" + }, + "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==", + "requires": { + "p-locate": "^5.0.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "requires": { + "p-limit": "^3.0.2" + } + }, + "schema-utils": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", + "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", + "requires": { + "@types/json-schema": "^7.0.4", + "ajv": "^6.12.2", + "ajv-keywords": "^3.4.1" + } + }, + "tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==" + } + } + }, + "react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "requires": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + } + }, + "react-error-overlay": { + "version": "6.0.11", + "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", + "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" + }, + "react-fast-compare": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz", + "integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==" + }, + "react-helmet-async": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-1.3.0.tgz", + "integrity": "sha512-9jZ57/dAn9t3q6hneQS0wukqC2ENOBgMNVEhb/ZG9ZSxUetzVIw4iAmEU38IaVg3QGYauQPhSeUTuIUtFglWpg==", + "requires": { + "@babel/runtime": "^7.12.5", + "invariant": "^2.2.4", + "prop-types": "^15.7.2", + "react-fast-compare": "^3.2.0", + "shallowequal": "^1.1.0" + } + }, + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "react-json-view": { + "version": "1.21.3", + "resolved": "https://registry.npmjs.org/react-json-view/-/react-json-view-1.21.3.tgz", + "integrity": "sha512-13p8IREj9/x/Ye4WI/JpjhoIwuzEgUAtgJZNBJckfzJt1qyh24BdTm6UQNGnyTq9dapQdrqvquZTo3dz1X6Cjw==", + "requires": { + "flux": "^4.0.1", + "react-base16-styling": "^0.6.0", + "react-lifecycles-compat": "^3.0.4", + "react-textarea-autosize": "^8.3.2" + } + }, + "react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, + "react-loadable": { + "version": "npm:@docusaurus/react-loadable@5.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz", + "integrity": "sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ==", + "requires": { + "@types/react": "*", + "prop-types": "^15.6.2" + } + }, + "react-loadable-ssr-addon-v5-slorber": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/react-loadable-ssr-addon-v5-slorber/-/react-loadable-ssr-addon-v5-slorber-1.0.1.tgz", + "integrity": "sha512-lq3Lyw1lGku8zUEJPDxsNm1AfYHBrO9Y1+olAYwpUJ2IGFBskM0DMKok97A6LWUpHm+o7IvQBOWu9MLenp9Z+A==", + "requires": { + "@babel/runtime": "^7.10.3" + } + }, + "react-router": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.3.4.tgz", + "integrity": "sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA==", + "requires": { + "@babel/runtime": "^7.12.13", + "history": "^4.9.0", + "hoist-non-react-statics": "^3.1.0", + "loose-envify": "^1.3.1", + "path-to-regexp": "^1.7.0", + "prop-types": "^15.6.2", + "react-is": "^16.6.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" + }, + "path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "requires": { + "isarray": "0.0.1" + } + }, + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + } + } + }, + "react-router-config": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/react-router-config/-/react-router-config-5.1.1.tgz", + "integrity": "sha512-DuanZjaD8mQp1ppHjgnnUnyOlqYXZVjnov/JzFhjLEwd3Z4dYjMSnqrEzzGThH47vpCOqPPwJM2FtthLeJ8Pbg==", + "requires": { + "@babel/runtime": "^7.1.2" + } + }, + "react-router-dom": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.3.4.tgz", + "integrity": "sha512-m4EqFMHv/Ih4kpcBCONHbkT68KoAeHN4p3lAGoNryfHi0dMy0kCzEZakiKRsvg5wHZ/JLrLW8o8KomWiz/qbYQ==", + "requires": { + "@babel/runtime": "^7.12.13", + "history": "^4.9.0", + "loose-envify": "^1.3.1", + "prop-types": "^15.6.2", + "react-router": "5.3.4", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0" + } + }, + "react-textarea-autosize": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.4.1.tgz", + "integrity": "sha512-aD2C+qK6QypknC+lCMzteOdIjoMbNlgSFmJjCV+DrfTPwp59i/it9mMNf2HDzvRjQgKAyBDPyLJhcrzElf2U4Q==", + "requires": { + "@babel/runtime": "^7.20.13", + "use-composed-ref": "^1.3.0", + "use-latest": "^1.2.1" + } + }, + "read": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", + "integrity": "sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ==", + "dev": true, + "requires": { + "mute-stream": "~0.0.4" + } + }, + "read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "requires": { + "pify": "^2.3.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true + } + } + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true + } + } + }, + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "dependencies": { + "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 + } + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "readdir-glob": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "requires": { + "minimatch": "^5.1.0" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "requires": { + "picomatch": "^2.2.1" + } + }, + "reading-time": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/reading-time/-/reading-time-1.5.0.tgz", + "integrity": "sha512-onYyVhBNr4CmAxFsKS7bz+uTLRakypIe4R+5A824vBSkQy/hB3fZepoVEf8OVAxzLvK+H/jm9TzpI3ETSm64Kg==" + }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", + "requires": { + "resolve": "^1.1.6" + } + }, + "recursive-readdir": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", + "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", + "requires": { + "minimatch": "^3.0.5" + } + }, + "regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" + }, + "regenerate-unicode-properties": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz", + "integrity": "sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==", + "requires": { + "regenerate": "^1.4.2" + } + }, + "regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, + "regenerator-transform": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", + "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", + "requires": { + "@babel/runtime": "^7.8.4" + } + }, + "regexp-tree": { + "version": "0.1.27", + "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", + "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==", + "dev": true + }, + "regexp.prototype.flags": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" + } + }, + "regexpu-core": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", + "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", + "requires": { + "@babel/regjsgen": "^0.8.0", + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.1.0", + "regjsparser": "^0.9.1", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" + } + }, + "registry-auth-token": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.2.tgz", + "integrity": "sha512-PC5ZysNb42zpFME6D/XlIgtNGdTl8bBOCw90xQLVMpzuuubJKYDWFAEuUNc+Cn8Z8724tg2SDhDRrkVEsqfDMg==", + "requires": { + "rc": "1.2.8" + } + }, + "registry-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", + "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", + "requires": { + "rc": "^1.2.8" + } + }, + "regjsparser": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", + "requires": { + "jsesc": "~0.5.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==" + } + } + }, + "relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==" + }, + "remark-emoji": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/remark-emoji/-/remark-emoji-2.2.0.tgz", + "integrity": "sha512-P3cj9s5ggsUvWw5fS2uzCHJMGuXYRb0NnZqYlNecewXt8QBU9n5vW3DUUKOhepS8F9CwdMx9B8a3i7pqFWAI5w==", + "requires": { + "emoticon": "^3.2.0", + "node-emoji": "^1.10.0", + "unist-util-visit": "^2.0.3" + } + }, + "remark-footnotes": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/remark-footnotes/-/remark-footnotes-2.0.0.tgz", + "integrity": "sha512-3Clt8ZMH75Ayjp9q4CorNeyjwIxHFcTkaektplKGl2A1jNGEUey8cKL0ZC5vJwfcD5GFGsNLImLG/NGzWIzoMQ==" + }, + "remark-mdx": { + "version": "1.6.22", + "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-1.6.22.tgz", + "integrity": "sha512-phMHBJgeV76uyFkH4rvzCftLfKCr2RZuF+/gmVcaKrpsihyzmhXjA0BEMDaPTXG5y8qZOKPVo83NAOX01LPnOQ==", + "requires": { + "@babel/core": "7.12.9", + "@babel/helper-plugin-utils": "7.10.4", + "@babel/plugin-proposal-object-rest-spread": "7.12.1", + "@babel/plugin-syntax-jsx": "7.12.1", + "@mdx-js/util": "1.6.22", + "is-alphabetical": "1.0.4", + "remark-parse": "8.0.3", + "unified": "9.2.0" + }, + "dependencies": { + "@babel/core": { + "version": "7.12.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.9.tgz", + "integrity": "sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ==", + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.12.5", + "@babel/helper-module-transforms": "^7.12.1", + "@babel/helpers": "^7.12.5", + "@babel/parser": "^7.12.7", + "@babel/template": "^7.12.7", + "@babel/traverse": "^7.12.9", + "@babel/types": "^7.12.7", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.2", + "lodash": "^4.17.19", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", + "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==" + }, + "@babel/plugin-proposal-object-rest-spread": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.12.1.tgz", + "integrity": "sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.0", + "@babel/plugin-transform-parameters": "^7.12.1" + } + }, + "@babel/plugin-syntax-jsx": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.1.tgz", + "integrity": "sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==" + }, + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==" + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==" + }, + "unified": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.0.tgz", + "integrity": "sha512-vx2Z0vY+a3YoTj8+pttM3tiJHCwY5UFbYdiWrwBEbHmK8pvsPj2rtAX2BFfgXen8T39CJWblWRDT4L5WGXtDdg==", + "requires": { + "bail": "^1.0.0", + "extend": "^3.0.0", + "is-buffer": "^2.0.0", + "is-plain-obj": "^2.0.0", + "trough": "^1.0.0", + "vfile": "^4.0.0" + } + } + } + }, + "remark-parse": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-8.0.3.tgz", + "integrity": "sha512-E1K9+QLGgggHxCQtLt++uXltxEprmWzNfg+MxpfHsZlrddKzZ/hZyWHDbK3/Ap8HJQqYJRXP+jHczdL6q6i85Q==", + "requires": { + "ccount": "^1.0.0", + "collapse-white-space": "^1.0.2", + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-whitespace-character": "^1.0.0", + "is-word-character": "^1.0.0", + "markdown-escapes": "^1.0.0", + "parse-entities": "^2.0.0", + "repeat-string": "^1.5.4", + "state-toggle": "^1.0.0", + "trim": "^0.0.3", + "trim-trailing-lines": "^1.0.0", + "unherit": "^1.0.4", + "unist-util-remove-position": "^2.0.0", + "vfile-location": "^3.0.0", + "xtend": "^4.0.1" + } + }, + "remark-squeeze-paragraphs": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/remark-squeeze-paragraphs/-/remark-squeeze-paragraphs-4.0.0.tgz", + "integrity": "sha512-8qRqmL9F4nuLPIgl92XUuxI3pFxize+F1H0e/W3llTk0UsjJaj01+RrirkMw7P21RKe4X6goQhYRSvNWX+70Rw==", + "requires": { + "mdast-squeeze-paragraphs": "^4.0.0" + } + }, + "renderkid": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", + "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", + "requires": { + "css-select": "^4.1.3", + "dom-converter": "^0.2.0", + "htmlparser2": "^6.1.0", + "lodash": "^4.17.21", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + } + } + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==" + }, + "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 + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" + }, + "require-like": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz", + "integrity": "sha512-oyrU88skkMtDdauHDuKVrgR+zuItqr6/c//FXzvmxRGMexSDc6hNvJInGW3LL46n+8b50RykrvwSUIIQH2LQ5A==" + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" + }, + "requizzle": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz", + "integrity": "sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==", + "requires": { + "lodash": "^4.17.21" + } + }, + "resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "requires": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==" + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + }, + "resolve-pathname": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz", + "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==" + }, + "resolve.exports": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", + "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==", + "dev": true + }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==" + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "requires": { + "glob": "^7.1.3" + } + }, + "robust-predicates": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", + "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==" + }, + "rollup": { + "version": "2.79.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", + "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", + "dev": true, + "requires": { + "fsevents": "~2.3.2" + } + }, + "rollup-plugin-copy": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-copy/-/rollup-plugin-copy-3.5.0.tgz", + "integrity": "sha512-wI8D5dvYovRMx/YYKtUNt3Yxaw4ORC9xo6Gt9t22kveWz1enG9QrhVlagzwrxSC455xD1dHMKhIJkbsQ7d48BA==", + "dev": true, + "requires": { + "@types/fs-extra": "^8.0.1", + "colorette": "^1.1.0", + "fs-extra": "^8.1.0", + "globby": "10.0.1", + "is-plain-object": "^3.0.0" + }, + "dependencies": { + "colorette": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", + "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", + "dev": true + }, + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "is-plain-object": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.1.tgz", + "integrity": "sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g==", + "dev": true + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + } + } + }, + "rollup-plugin-peer-deps-external": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/rollup-plugin-peer-deps-external/-/rollup-plugin-peer-deps-external-2.2.4.tgz", + "integrity": "sha512-AWdukIM1+k5JDdAqV/Cxd+nejvno2FVLVeZ74NKggm3Q5s9cbbcOgUPGdbxPi4BXu7xGaZ8HG12F+thImYu/0g==", + "dev": true + }, + "rollup-plugin-postcss": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/rollup-plugin-postcss/-/rollup-plugin-postcss-4.0.2.tgz", + "integrity": "sha512-05EaY6zvZdmvPUDi3uCcAQoESDcYnv8ogJJQRp6V5kZ6J6P7uAVJlrTZcaaA20wTH527YTnKfkAoPxWI/jPp4w==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "concat-with-sourcemaps": "^1.1.0", + "cssnano": "^5.0.1", + "import-cwd": "^3.0.0", + "p-queue": "^6.6.2", + "pify": "^5.0.0", + "postcss-load-config": "^3.0.0", + "postcss-modules": "^4.0.0", + "promise.series": "^0.2.0", + "resolve": "^1.19.0", + "rollup-pluginutils": "^2.8.2", + "safe-identifier": "^0.4.2", + "style-inject": "^0.3.0" + } + }, + "rollup-plugin-typescript2": { + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/rollup-plugin-typescript2/-/rollup-plugin-typescript2-0.34.1.tgz", + "integrity": "sha512-P4cHLtGikESmqi1CA+tdMDUv8WbQV48mzPYt77TSTOPJpERyZ9TXdDgjSDix8Fkqce6soYz3+fa4lrC93IEkcw==", + "dev": true, + "requires": { + "@rollup/pluginutils": "^4.1.2", + "find-cache-dir": "^3.3.2", + "fs-extra": "^10.0.0", + "semver": "^7.3.7", + "tslib": "^2.4.0" + }, + "dependencies": { + "@rollup/pluginutils": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz", + "integrity": "sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==", + "dev": true, + "requires": { + "estree-walker": "^2.0.1", + "picomatch": "^2.2.2" + } + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "rollup-pluginutils": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz", + "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==", + "dev": true, + "requires": { + "estree-walker": "^0.6.1" + }, + "dependencies": { + "estree-walker": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", + "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==", + "dev": true + } + } + }, + "rtl-detect": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/rtl-detect/-/rtl-detect-1.0.4.tgz", + "integrity": "sha512-EBR4I2VDSSYr7PkBmFy04uhycIpDKp+21p/jARYXlCSjQksTBQcJ0HFUPOO79EPPH5JS6VAhiIQbycf0O3JAxQ==" + }, + "rtlcss": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-3.5.0.tgz", + "integrity": "sha512-wzgMaMFHQTnyi9YOwsx9LjOxYXJPzS8sYnFaKm6R5ysvTkwzHiB0vxnbHwchHQT65PTdBjDG21/kQBWI7q9O7A==", + "requires": { + "find-up": "^5.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.3.11", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "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==", + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "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==", + "requires": { + "p-locate": "^5.0.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "requires": { + "p-limit": "^3.0.2" + } + } + } + }, + "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==", + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" + }, + "rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "requires": { + "tslib": "^2.1.0" + } + }, + "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==" + }, + "safe-identifier": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/safe-identifier/-/safe-identifier-0.4.2.tgz", + "integrity": "sha512-6pNbSMW6OhAi9j+N8V+U715yBQsaWJ7eyEUaOrawX+isg5ZxhUlV1NipNtgaKHmFGiABwt+ZF04Ii+3Xjkg+8w==", + "dev": true + }, + "safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sass": { + "version": "1.70.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.70.0.tgz", + "integrity": "sha512-uUxNQ3zAHeAx5nRFskBnrWzDUJrrvpCPD5FNAoRvTi0WwremlheES3tg+56PaVtCs5QDRX5CBLxxKMDJMEa1WQ==", + "dev": true, + "requires": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + } + }, + "sass-loader": { + "version": "12.6.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz", + "integrity": "sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA==", + "dev": true, + "requires": { + "klona": "^2.0.4", + "neo-async": "^2.6.2" + } + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "requires": { + "xmlchars": "^2.2.0" + } + }, + "scheduler": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "requires": { + "loose-envify": "^1.1.0" + } + }, + "schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "requires": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + } + }, + "section-matter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", + "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==", + "requires": { + "extend-shallow": "^2.0.1", + "kind-of": "^6.0.0" + } + }, + "secure-compare": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz", + "integrity": "sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==", + "dev": true + }, + "select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==" + }, + "selfsigned": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.1.1.tgz", + "integrity": "sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ==", + "requires": { + "node-forge": "^1" + } + }, + "semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "semver-diff": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", + "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", + "requires": { + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "requires": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + } + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, + "serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "requires": { + "randombytes": "^2.1.0" + } + }, + "serve-handler": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.5.tgz", + "integrity": "sha512-ijPFle6Hwe8zfmBxJdE+5fta53fdIY0lHISJvuikXB3VYFafRjMRpOffSPvCYsbKyBA7pvy9oYr/BT1O3EArlg==", + "requires": { + "bytes": "3.0.0", + "content-disposition": "0.5.2", + "fast-url-parser": "1.1.3", + "mime-types": "2.1.18", + "minimatch": "3.1.2", + "path-is-inside": "1.0.2", + "path-to-regexp": "2.2.1", + "range-parser": "1.2.0" + }, + "dependencies": { + "content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==" + }, + "mime-db": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", + "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==" + }, + "mime-types": { + "version": "2.1.18", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", + "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", + "requires": { + "mime-db": "~1.33.0" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "path-to-regexp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.2.1.tgz", + "integrity": "sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ==" + }, + "range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==" + } + } + }, + "serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", + "requires": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==" + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==" + } + } + }, + "serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + } + }, + "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==" + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "requires": { + "kind-of": "^6.0.2" + } + }, + "shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + }, + "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==", + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" + }, + "shell-quote": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.0.tgz", + "integrity": "sha512-QHsz8GgQIGKlRi24yFc6a6lN69Idnx634w49ay6+jA5yFh7a1UY+4Rp6HPx/L/1zcEDPEij8cIsiqR6bQsE5VQ==" + }, + "shelljs": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", + "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", + "requires": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + } + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true + }, + "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==" + }, + "simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "dev": true, + "optional": true + }, + "simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "dev": true, + "optional": true, + "requires": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "sirv": { + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-1.0.19.tgz", + "integrity": "sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ==", + "requires": { + "@polka/url": "^1.0.0-next.20", + "mrmime": "^1.0.0", + "totalist": "^1.0.0" + } + }, + "sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" + }, + "sitemap": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-7.1.1.tgz", + "integrity": "sha512-mK3aFtjz4VdJN0igpIJrinf3EO8U8mxOPsTBzSsy06UtjZQJ3YY3o3Xa7zSc5nMqcMrRwlChHZ18Kxg0caiPBg==", + "requires": { + "@types/node": "^17.0.5", + "@types/sax": "^1.2.1", + "arg": "^5.0.0", + "sax": "^1.2.4" + }, + "dependencies": { + "@types/node": { + "version": "17.0.45", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz", + "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==" + }, + "arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" + } + } + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" + }, + "smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "optional": true + }, + "snake-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", + "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "dev": true, + "requires": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "requires": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "socks": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", + "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", + "optional": true, + "requires": { + "ip": "^2.0.0", + "smart-buffer": "^4.2.0" + } + }, + "socks-proxy-agent": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", + "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", + "optional": true, + "requires": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + } + }, + "sort-css-media-queries": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/sort-css-media-queries/-/sort-css-media-queries-2.1.0.tgz", + "integrity": "sha512-IeWvo8NkNiY2vVYdPa27MCQiR0MN0M80johAYFVxWWXQ44KU84WNxjslwBHmc/7ZL2ccwkM7/e6S5aiKZXm7jA==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "source-map-js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==" + }, + "source-map-loader": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-3.0.2.tgz", + "integrity": "sha512-BokxPoLjyl3iOrgkWaakaxqnelAJSS+0V+De0kKIq6lyWrXuiPgYTGp6z3iHmqljKAaLXwZa+ctD8GccRJeVvg==", + "dev": true, + "requires": { + "abab": "^2.0.5", + "iconv-lite": "^0.6.3", + "source-map-js": "^1.0.1" + } + }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "dev": true + }, + "space-separated-tokens": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz", + "integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==" + }, + "spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "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 + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz", + "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==", + "dev": true + }, + "spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "requires": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + } + }, + "spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "requires": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "split2": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz", + "integrity": "sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ==" + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + }, + "sqlite3": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.1.5.tgz", + "integrity": "sha512-7sP16i4wI+yKnGOO2q2ijze7EjQ9US+Vw7DYYwxfFtqNZDGgBcEw0oeDaDvUTq66uJOzVd/z6MkIg+c9erSJKg==", + "requires": { + "@mapbox/node-pre-gyp": "^1.0.0", + "node-addon-api": "^4.2.0", + "node-gyp": "8.x", + "tar": "^6.1.11" + }, + "dependencies": { + "node-addon-api": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", + "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==" + } + } + }, + "ssri": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "optional": true, + "requires": { + "minipass": "^3.1.1" + } + }, + "stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==" + }, + "stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "requires": { + "escape-string-regexp": "^2.0.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true + } + } + }, + "stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true + }, + "state-toggle": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/state-toggle/-/state-toggle-1.0.3.tgz", + "integrity": "sha512-d/5Z4/2iiCnHw6Xzghyhb+GcmF89bxwgXG60wjIiZaxnymbyOmI8Hk4VqHXiVVp6u2ysaskFfXg3ekCj4WNftQ==" + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" + }, + "std-env": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz", + "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==" + }, + "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==", + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "string-hash": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/string-hash/-/string-hash-1.1.3.tgz", + "integrity": "sha512-kJUvRUFK49aub+a7T1nNE66EJbZBMnBgoC1UbCZ5n6bsZKBRga4KgBRTMn/pFkeCZSYtNeSyMxPDM0AXWELk2A==", + "dev": true + }, + "string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "requires": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + } + }, + "string-natural-compare": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/string-natural-compare/-/string-natural-compare-3.0.1.tgz", + "integrity": "sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==", + "dev": true + }, + "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==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "string.prototype.matchall": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz", + "integrity": "sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "regexp.prototype.flags": "^1.4.3", + "side-channel": "^1.0.4" + } + }, + "string.prototype.trimend": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", + "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + } + }, + "string.prototype.trimstart": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", + "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + } + }, + "stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "requires": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "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 + }, + "strip-bom-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", + "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==" + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==" + }, + "strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "requires": { + "min-indent": "^1.0.0" + } + }, + "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==" + }, + "strip-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.1.0.tgz", + "integrity": "sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw==", + "dev": true, + "requires": { + "js-tokens": "^9.0.0" + }, + "dependencies": { + "js-tokens": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.0.tgz", + "integrity": "sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==", + "dev": true + } + } + }, + "strong-log-transformer": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz", + "integrity": "sha512-B3Hgul+z0L9a236FAUC9iZsL+nVHgoCJnqCbN588DjYxvGXaXaaFbfmQ/JhvKjZwsOukuR72XbHv71Qkug0HxA==", + "dev": true, + "requires": { + "duplexer": "^0.1.1", + "minimist": "^1.2.0", + "through": "^2.3.4" + } + }, + "style-inject": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/style-inject/-/style-inject-0.3.0.tgz", + "integrity": "sha512-IezA2qp+vcdlhJaVm5SOdPPTUu0FCEqfNSli2vRuSIBbu5Nq5UvygTk/VzeCqfLz2Atj3dVII5QBKGZRZ0edzw==", + "dev": true + }, + "style-loader": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.4.tgz", + "integrity": "sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w==", + "dev": true + }, + "style-to-object": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.3.0.tgz", + "integrity": "sha512-CzFnRRXhzWIdItT3OmF8SQfWyahHhjq3HwcMNCNLn+N7klOOqPjMeG/4JSu77D7ypZdGvSzvkrbyeTMizz2VrA==", + "requires": { + "inline-style-parser": "0.1.1" + } + }, + "stylehacks": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", + "integrity": "sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==", + "requires": { + "browserslist": "^4.21.4", + "postcss-selector-parser": "^6.0.4" + } + }, + "stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" + }, + "stylus": { + "version": "0.59.0", + "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.59.0.tgz", + "integrity": "sha512-lQ9w/XIOH5ZHVNuNbWW8D822r+/wBSO/d6XvtyHLF7LW4KaCIDeVbvn5DF8fGCJAUCwVhVi/h6J0NUcnylUEjg==", + "dev": true, + "requires": { + "@adobe/css-tools": "^4.0.1", + "debug": "^4.3.2", + "glob": "^7.1.6", + "sax": "~1.2.4", + "source-map": "^0.7.3" + }, + "dependencies": { + "source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true + } + } + }, + "stylus-loader": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-7.1.3.tgz", + "integrity": "sha512-TY0SKwiY7D2kMd3UxaWKSf3xHF0FFN/FAfsSqfrhxRT/koXTwffq2cgEWDkLQz7VojMu7qEEHt5TlMjkPx9UDw==", + "dev": true, + "requires": { + "fast-glob": "^3.2.12", + "normalize-path": "^3.0.0" + }, + "dependencies": { + "fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + } + } + }, + "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==", + "requires": { + "has-flag": "^4.0.0" + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" + }, + "svg-parser": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", + "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==" + }, + "svgo": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", + "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "requires": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^4.1.3", + "css-tree": "^1.1.3", + "csso": "^4.2.0", + "picocolors": "^1.0.0", + "stable": "^0.1.8" + }, + "dependencies": { + "commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==" + } + } + }, + "symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true + }, + "tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==" + }, + "tar": { + "version": "6.1.12", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.12.tgz", + "integrity": "sha512-jU4TdemS31uABHd+Lt5WEYJuzn+TJTCBLljvIAHZOz6M9Os5pJ4dD+vRFLxPa/n3T0iEFzpi+0x1UfuDZYbRMw==", + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + } + }, + "tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "dev": true, + "optional": true, + "requires": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + }, + "dependencies": { + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true, + "optional": true + } + } + }, + "tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "requires": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + } + }, + "terser": { + "version": "5.27.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.27.0.tgz", + "integrity": "sha512-bi1HRwVRskAjheeYl291n3JC4GgO/Ty4z1nVs5AAsmonJulGxpSektecnNedrwK9C7vpvVtcX3cw00VSLt7U2A==", + "requires": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + } + } + }, + "terser-webpack-plugin": { + "version": "5.3.10", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", + "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", + "requires": { + "@jridgewell/trace-mapping": "^0.3.20", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.1", + "terser": "^5.26.0" + }, + "dependencies": { + "jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + } + }, + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, + "thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" + }, + "tiny-invariant": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", + "integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==" + }, + "tiny-warning": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" + }, + "tinybench": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.8.0.tgz", + "integrity": "sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw==", + "dev": true + }, + "tinypool": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.4.tgz", + "integrity": "sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==", + "dev": true + }, + "tinyspy": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.1.tgz", + "integrity": "sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==", + "dev": true + }, + "tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "requires": { + "rimraf": "^3.0.0" + } + }, + "tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==" + }, + "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==", + "requires": { + "is-number": "^7.0.0" + } + }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" + }, + "totalist": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-1.1.0.tgz", + "integrity": "sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==" + }, + "tough-cookie": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "dev": true, + "requires": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "dependencies": { + "universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "dev": true + } + } + }, + "tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "dev": true, + "requires": { + "punycode": "^2.1.1" + } + }, + "traverse": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", + "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==" + }, + "trim": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.3.tgz", + "integrity": "sha512-h82ywcYhHK7veeelXrCScdH7HkWfbIT1D/CgYO+nmDarz3SGNssVBMws6jU16Ga60AJCRAvPV6w6RLuNerQqjg==" + }, + "trim-trailing-lines": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-1.1.4.tgz", + "integrity": "sha512-rjUWSqnfTNrjbB9NQWfPMH/xRK1deHeGsHoVfpxJ++XeYXE0d6B1En37AHfw3jtfTU7dzMzZL2jjpe8Qb5gLIQ==" + }, + "trough": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz", + "integrity": "sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==" + }, + "ts-api-utils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "dev": true + }, + "ts-dedent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz", + "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==" + }, + "ts-loader": { + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.1.tgz", + "integrity": "sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "enhanced-resolve": "^5.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4", + "source-map": "^0.7.4" + }, + "dependencies": { + "source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true + } + } + }, + "ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "requires": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + } + }, + "tsconfig-paths": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", + "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", + "dev": true, + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "dependencies": { + "json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true + } + } + }, + "tsconfig-paths-webpack-plugin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.0.0.tgz", + "integrity": "sha512-fw/7265mIWukrSHd0i+wSwx64kYUSAKPfxRDksjKIYTxSAp9W9/xcZVBF4Kl0eqQd5eBpAQ/oQrc5RyM/0c1GQ==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "enhanced-resolve": "^5.7.0", + "tsconfig-paths": "^4.0.0" + }, + "dependencies": { + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true + }, + "tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "requires": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + } + } + }, + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "dev": true + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, + "type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typed-assert": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/typed-assert/-/typed-assert-1.0.9.tgz", + "integrity": "sha512-KNNZtayBCtmnNmbo5mG47p1XsCyrx6iVqomjcZnec/1Y5GGARaxPs6r49RnSPeUP3YjNYiU9sQHAtY4BBvnZwg==", + "dev": true + }, + "typed-rest-client": { + "version": "1.8.9", + "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.8.9.tgz", + "integrity": "sha512-uSmjE38B80wjL85UFX3sTYEUlvZ1JgCRhsWj/fJ4rZ0FqDUFoIuodtiVeE+cUqiVTOKPdKrp/sdftD15MDek6g==", + "dev": true, + "requires": { + "qs": "^6.9.1", + "tunnel": "0.0.6", + "underscore": "^1.12.1" + } + }, + "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==", + "requires": { + "is-typedarray": "^1.0.0" + } + }, + "typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "dev": true + }, + "ua-parser-js": { + "version": "1.0.35", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.35.tgz", + "integrity": "sha512-fKnGuqmTBnIE+/KXSzCn4db8RTigUzw1AN0DmdU6hJovUTbYJKyqj+8Mt1c4VfRDnOVJnENmfYkIPZ946UrSAA==" + }, + "uc.micro": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==" + }, + "ufo": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.3.tgz", + "integrity": "sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==", + "dev": true + }, + "uglify-js": { + "version": "3.17.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", + "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==" + }, + "unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + } + }, + "underscore": { + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", + "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==" + }, + "undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, + "unherit": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/unherit/-/unherit-1.1.3.tgz", + "integrity": "sha512-Ft16BJcnapDKp0+J/rqFC3Rrk6Y/Ng4nzsC028k2jdDII/rdZ7Wd3pPT/6+vIIxRagwRc9K0IUX0Ra4fKvw+WQ==", + "requires": { + "inherits": "^2.0.0", + "xtend": "^4.0.0" + } + }, + "unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==" + }, + "unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "requires": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + } + }, + "unicode-match-property-value-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", + "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==" + }, + "unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==" + }, + "unified": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.2.tgz", + "integrity": "sha512-Sg7j110mtefBD+qunSLO1lqOEKdrwBFBrR6Qd8f4uwkhWNlbkaqwHse6e7QvD3AP/MNoJdEDLaf8OxYyoWgorQ==", + "requires": { + "bail": "^1.0.0", + "extend": "^3.0.0", + "is-buffer": "^2.0.0", + "is-plain-obj": "^2.0.0", + "trough": "^1.0.0", + "vfile": "^4.0.0" + }, + "dependencies": { + "is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==" + }, + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==" + } + } + }, + "union": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz", + "integrity": "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==", + "dev": true, + "requires": { + "qs": "^6.4.0" + } + }, + "unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "optional": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "optional": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "requires": { + "crypto-random-string": "^2.0.0" + } + }, + "unist-builder": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-builder/-/unist-builder-2.0.3.tgz", + "integrity": "sha512-f98yt5pnlMWlzP539tPc4grGMsFaQQlP/vM396b00jngsiINumNmsY8rkXjfoi1c6QaM8nQ3vaGDuoKWbe/1Uw==" + }, + "unist-util-generated": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-1.1.6.tgz", + "integrity": "sha512-cln2Mm1/CZzN5ttGK7vkoGw+RZ8VcUH6BtGbq98DDtRGquAAOXig1mrBQYelOwMXYS8rK+vZDyyojSjp7JX+Lg==" + }, + "unist-util-is": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.1.0.tgz", + "integrity": "sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==" + }, + "unist-util-position": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-3.1.0.tgz", + "integrity": "sha512-w+PkwCbYSFw8vpgWD0v7zRCl1FpY3fjDSQ3/N/wNd9Ffa4gPi8+4keqt99N3XW6F99t/mUzp2xAhNmfKWp95QA==" + }, + "unist-util-remove": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unist-util-remove/-/unist-util-remove-2.1.0.tgz", + "integrity": "sha512-J8NYPyBm4baYLdCbjmf1bhPu45Cr1MWTm77qd9istEkzWpnN6O9tMsEbB2JhNnBCqGENRqEWomQ+He6au0B27Q==", + "requires": { + "unist-util-is": "^4.0.0" + } + }, + "unist-util-remove-position": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-2.0.1.tgz", + "integrity": "sha512-fDZsLYIe2uT+oGFnuZmy73K6ZxOPG/Qcm+w7jbEjaFcJgbQ6cqjs/eSPzXhsmGpAsWPkqZM9pYjww5QTn3LHMA==", + "requires": { + "unist-util-visit": "^2.0.0" + } + }, + "unist-util-stringify-position": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz", + "integrity": "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==", + "requires": { + "@types/unist": "^2.0.2" + } + }, + "unist-util-visit": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.3.tgz", + "integrity": "sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==", + "requires": { + "@types/unist": "^2.0.0", + "unist-util-is": "^4.0.0", + "unist-util-visit-parents": "^3.0.0" + } + }, + "unist-util-visit-parents": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz", + "integrity": "sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==", + "requires": { + "@types/unist": "^2.0.0", + "unist-util-is": "^4.0.0" + } + }, + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" + }, + "unzipper": { + "version": "0.10.14", + "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.14.tgz", + "integrity": "sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g==", + "requires": { + "big-integer": "^1.6.17", + "binary": "~0.3.0", + "bluebird": "~3.4.1", + "buffer-indexof-polyfill": "~1.0.0", + "duplexer2": "~0.1.4", + "fstream": "^1.0.12", + "graceful-fs": "^4.2.2", + "listenercount": "~1.0.1", + "readable-stream": "~2.3.6", + "setimmediate": "~1.0.4" + }, + "dependencies": { + "bluebird": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", + "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==" + }, + "readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "requires": { + "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" + } + }, + "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==" + }, + "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==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "requires": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + } + }, + "update-notifier": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-5.1.0.tgz", + "integrity": "sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw==", + "requires": { + "boxen": "^5.0.0", + "chalk": "^4.1.0", + "configstore": "^5.0.1", + "has-yarn": "^2.1.0", + "import-lazy": "^2.1.0", + "is-ci": "^2.0.0", + "is-installed-globally": "^0.4.0", + "is-npm": "^5.0.0", + "is-yarn-global": "^0.3.0", + "latest-version": "^5.1.0", + "pupa": "^2.1.1", + "semver": "^7.3.4", + "semver-diff": "^3.1.1", + "xdg-basedir": "^4.0.0" + }, + "dependencies": { + "boxen": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", + "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", + "requires": { + "ansi-align": "^3.0.0", + "camelcase": "^6.2.0", + "chalk": "^4.1.0", + "cli-boxes": "^2.2.1", + "string-width": "^4.2.2", + "type-fest": "^0.20.2", + "widest-line": "^3.1.0", + "wrap-ansi": "^7.0.0" + } + }, + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==" + }, + "cli-boxes": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", + "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==" + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==" + }, + "widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "requires": { + "string-width": "^4.0.0" + } + } + } + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "requires": { + "punycode": "^2.1.0" + } + }, + "url-join": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", + "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", + "dev": true + }, + "url-loader": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz", + "integrity": "sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==", + "requires": { + "loader-utils": "^2.0.0", + "mime-types": "^2.1.27", + "schema-utils": "^3.0.0" + }, + "dependencies": { + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dev": true, + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "use-composed-ref": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.3.0.tgz", + "integrity": "sha512-GLMG0Jc/jiKov/3Ulid1wbv3r54K9HlMW29IWcDFPEqFkSO2nS0MuefWgMJpeHQ9YJeXDL3ZUF+P3jdXlZX/cQ==" + }, + "use-isomorphic-layout-effect": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz", + "integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==" + }, + "use-latest": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/use-latest/-/use-latest-1.2.1.tgz", + "integrity": "sha512-xA+AVm/Wlg3e2P/JiItTziwS7FK92LWrDB0p+hgXloIMuVCeJJ8v6f0eeHyPZaJrM+usM1FkFfbNCrJGs8A/zw==", + "requires": { + "use-isomorphic-layout-effect": "^1.1.1" + } + }, + "use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==" + }, + "util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "requires": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "utila": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==" + }, + "utility-types": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.10.0.tgz", + "integrity": "sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + }, + "v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "v8-to-istanbul": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz", + "integrity": "sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "validate-npm-package-name": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.0.tgz", + "integrity": "sha512-YuKoXDAhBYxY7SfOKxHBDoSyENFeW5VvIIQp2TGQuit8gpK6MnWaQelBKxso72DoxTZfZdcP3W90LqpSkgPzLQ==", + "dev": true, + "requires": { + "builtins": "^5.0.0" + } + }, + "value-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", + "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" + }, + "vfile": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.2.1.tgz", + "integrity": "sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==", + "requires": { + "@types/unist": "^2.0.0", + "is-buffer": "^2.0.0", + "unist-util-stringify-position": "^2.0.0", + "vfile-message": "^2.0.0" + }, + "dependencies": { + "is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==" + } + } + }, + "vfile-location": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-3.2.0.tgz", + "integrity": "sha512-aLEIZKv/oxuCDZ8lkJGhuhztf/BW4M+iHdCwglA/eWc+vtuRFJj8EtgceYFX4LRjOhCAAiNHsKGssC6onJ+jbA==" + }, + "vfile-message": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.4.tgz", + "integrity": "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==", + "requires": { + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^2.0.0" + } + }, + "vite": { + "version": "5.0.13", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.0.13.tgz", + "integrity": "sha512-/9ovhv2M2dGTuA+dY93B9trfyWMDRQw2jdVBhHNP6wr0oF34wG2i/N55801iZIpgUpnHDm4F/FabGQLyc+eOgg==", + "dev": true, + "requires": { + "esbuild": "^0.19.3", + "fsevents": "~2.3.3", + "postcss": "^8.4.32", + "rollup": "^4.2.0" + }, + "dependencies": { + "rollup": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.17.2.tgz", + "integrity": "sha512-/9ClTJPByC0U4zNLowV1tMBe8yMEAxewtR3cUNX5BoEpGH3dQEWpJLr6CLp0fPdYRF/fzVOgvDb1zXuakwF5kQ==", + "dev": true, + "requires": { + "@rollup/rollup-android-arm-eabi": "4.17.2", + "@rollup/rollup-android-arm64": "4.17.2", + "@rollup/rollup-darwin-arm64": "4.17.2", + "@rollup/rollup-darwin-x64": "4.17.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.17.2", + "@rollup/rollup-linux-arm-musleabihf": "4.17.2", + "@rollup/rollup-linux-arm64-gnu": "4.17.2", + "@rollup/rollup-linux-arm64-musl": "4.17.2", + "@rollup/rollup-linux-powerpc64le-gnu": "4.17.2", + "@rollup/rollup-linux-riscv64-gnu": "4.17.2", + "@rollup/rollup-linux-s390x-gnu": "4.17.2", + "@rollup/rollup-linux-x64-gnu": "4.17.2", + "@rollup/rollup-linux-x64-musl": "4.17.2", + "@rollup/rollup-win32-arm64-msvc": "4.17.2", + "@rollup/rollup-win32-ia32-msvc": "4.17.2", + "@rollup/rollup-win32-x64-msvc": "4.17.2", + "@types/estree": "1.0.5", + "fsevents": "~2.3.2" + } + } + } + }, + "vite-node": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.5.3.tgz", + "integrity": "sha512-axFo00qiCpU/JLd8N1gu9iEYL3xTbMbMrbe5nDp9GL0nb6gurIdZLkkFogZXWnE8Oyy5kfSLwNVIcVsnhE7lgQ==", + "dev": true, + "requires": { + "cac": "^6.7.14", + "debug": "^4.3.4", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "vite": "^5.0.0" + } + }, + "vitest": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.5.3.tgz", + "integrity": "sha512-2oM7nLXylw3mQlW6GXnRriw+7YvZFk/YNV8AxIC3Z3MfFbuziLGWP9GPxxu/7nRlXhqyxBikpamr+lEEj1sUEw==", + "dev": true, + "requires": { + "@vitest/expect": "1.5.3", + "@vitest/runner": "1.5.3", + "@vitest/snapshot": "1.5.3", + "@vitest/spy": "1.5.3", + "@vitest/utils": "1.5.3", + "acorn-walk": "^8.3.2", + "chai": "^4.3.10", + "debug": "^4.3.4", + "execa": "^8.0.1", + "local-pkg": "^0.5.0", + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "std-env": "^3.5.0", + "strip-literal": "^2.0.0", + "tinybench": "^2.5.1", + "tinypool": "^0.8.3", + "vite": "^5.0.0", + "vite-node": "1.5.3", + "why-is-node-running": "^2.2.2" + }, + "dependencies": { + "execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + } + }, + "get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true + }, + "human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true + }, + "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 + }, + "magic-string": { + "version": "0.30.10", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz", + "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==", + "dev": true, + "requires": { + "@jridgewell/sourcemap-codec": "^1.4.15" + } + }, + "mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true + }, + "npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "dev": true, + "requires": { + "path-key": "^4.0.0" + } + }, + "onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "requires": { + "mimic-fn": "^4.0.0" + } + }, + "path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true + }, + "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 + }, + "strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true + } + } + }, + "vscode": { + "version": "npm:@codingame/monaco-vscode-api@1.69.13", + "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-api/-/monaco-vscode-api-1.69.13.tgz", + "integrity": "sha512-7+dQbQ5O8mQhFyUcAiiJkCotNjZUzRxh4NMBKc/BSIFi0jG47bay+jP/+ngsmxHBapjs/xUAPaKSGNnf9WBmAA==" + }, + "vscode-jsonrpc": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.0.2.tgz", + "integrity": "sha512-RY7HwI/ydoC1Wwg4gJ3y6LpU9FJRZAUnTYMXthqhFXXu77ErDd/xkREpGuk4MyYkk4a+XDWAMqe0S3KkelYQEQ==" + }, + "vscode-languageclient": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-9.0.1.tgz", + "integrity": "sha512-JZiimVdvimEuHh5olxhxkht09m3JzUGwggb5eRUkzzJhZ2KjCN0nh55VfiED9oez9DyF8/fz1g1iBV3h+0Z2EA==", + "requires": { + "minimatch": "^5.1.0", + "semver": "^7.3.7", + "vscode-languageserver-protocol": "3.17.5" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "vscode-jsonrpc": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", + "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==" + }, + "vscode-languageserver-protocol": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", + "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", + "requires": { + "vscode-jsonrpc": "8.2.0", + "vscode-languageserver-types": "3.17.5" + } + }, + "vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==" + } + } + }, + "vscode-languageserver": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz", + "integrity": "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==", + "requires": { + "vscode-languageserver-protocol": "3.17.5" + }, + "dependencies": { + "vscode-jsonrpc": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", + "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==" + }, + "vscode-languageserver-protocol": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", + "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", + "requires": { + "vscode-jsonrpc": "8.2.0", + "vscode-languageserver-types": "3.17.5" + } + }, + "vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==" + } + } + }, + "vscode-languageserver-protocol": { + "version": "3.17.2", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.2.tgz", + "integrity": "sha512-8kYisQ3z/SQ2kyjlNeQxbkkTNmVFoQCqkmGrzLH6A9ecPlgTbp3wDTnUNqaUxYr4vlAcloxx8zwy7G5WdguYNg==", + "requires": { + "vscode-jsonrpc": "8.0.2", + "vscode-languageserver-types": "3.17.2" + } + }, + "vscode-languageserver-textdocument": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.11.tgz", + "integrity": "sha512-X+8T3GoiwTVlJbicx/sIAF+yuJAqz8VvwJyoMVhwEMoEKE/fkDmrqUgDMyBECcM2A2frVZIUj5HI/ErRXCfOeA==" + }, + "vscode-languageserver-types": { + "version": "3.17.2", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.2.tgz", + "integrity": "sha512-zHhCWatviizPIq9B7Vh9uvrH6x3sK8itC84HkamnBWoDFJtzBf7SWlpLCZUit72b3os45h6RWQNC9xHRDF8dRA==" + }, + "vscode-uri": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", + "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==" + }, + "w3c-xmlserializer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", + "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==", + "dev": true, + "requires": { + "xml-name-validator": "^4.0.0" + } + }, + "wait-on": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-6.0.1.tgz", + "integrity": "sha512-zht+KASY3usTY5u2LgaNqn/Cd8MukxLGjdcZxT2ns5QzDmTFc4XoWBgC+C/na+sMRZTuVygQoMYwdcVjHnYIVw==", + "requires": { + "axios": "^0.25.0", + "joi": "^17.6.0", + "lodash": "^4.17.21", + "minimist": "^1.2.5", + "rxjs": "^7.5.4" + }, + "dependencies": { + "axios": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.25.0.tgz", + "integrity": "sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==", + "requires": { + "follow-redirects": "^1.14.7" + } + } + } + }, + "walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "requires": { + "makeerror": "1.0.12" + } + }, + "watchpack": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "requires": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + } + }, + "wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "requires": { + "minimalistic-assert": "^1.0.0" + } + }, + "wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "requires": { + "defaults": "^1.0.3" + } + }, + "web-namespaces": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-1.1.4.tgz", + "integrity": "sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw==" + }, + "web-worker": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.2.0.tgz", + "integrity": "sha512-PgF341avzqyx60neE9DD+XS26MMNMoUQRz9NOZwW32nPQrF6p77f1htcnjBSEV8BGMKZ16choqUG4hyI0Hx7mA==" + }, + "webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true + }, + "webpack": { + "version": "5.90.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.90.0.tgz", + "integrity": "sha512-bdmyXRCXeeNIePv6R6tGPyy20aUobw4Zy8r0LUS2EWO+U+Ke/gYDgsCh7bl5rB6jPpr4r0SZa6dPxBxLooDT3w==", + "requires": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^1.0.5", + "@webassemblyjs/ast": "^1.11.5", + "@webassemblyjs/wasm-edit": "^1.11.5", + "@webassemblyjs/wasm-parser": "^1.11.5", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.9.0", + "browserslist": "^4.21.10", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.15.0", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.2.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.10", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" + }, + "dependencies": { + "schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "webpack-bundle-analyzer": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.8.0.tgz", + "integrity": "sha512-ZzoSBePshOKhr+hd8u6oCkZVwpVaXgpw23ScGLFpR6SjYI7+7iIWYarjN6OEYOfRt8o7ZyZZQk0DuMizJ+LEIg==", + "requires": { + "@discoveryjs/json-ext": "0.5.7", + "acorn": "^8.0.4", + "acorn-walk": "^8.0.0", + "chalk": "^4.1.0", + "commander": "^7.2.0", + "gzip-size": "^6.0.0", + "lodash": "^4.17.20", + "opener": "^1.5.2", + "sirv": "^1.0.7", + "ws": "^7.3.1" + }, + "dependencies": { + "commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==" + }, + "ws": { + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==" + } + } + }, + "webpack-dev-middleware": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz", + "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==", + "requires": { + "colorette": "^2.0.10", + "memfs": "^3.4.3", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz", + "integrity": "sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + } + } + }, + "webpack-dev-server": { + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.11.1.tgz", + "integrity": "sha512-lILVz9tAUy1zGFwieuaQtYiadImb5M3d+H+L1zDYalYoDl0cksAB1UNyuE5MMWJrG6zR1tXkCP2fitl7yoUJiw==", + "requires": { + "@types/bonjour": "^3.5.9", + "@types/connect-history-api-fallback": "^1.3.5", + "@types/express": "^4.17.13", + "@types/serve-index": "^1.9.1", + "@types/serve-static": "^1.13.10", + "@types/sockjs": "^0.3.33", + "@types/ws": "^8.5.1", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.0.11", + "chokidar": "^3.5.3", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^2.0.0", + "default-gateway": "^6.0.3", + "express": "^4.17.3", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^2.0.3", + "ipaddr.js": "^2.0.1", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "rimraf": "^3.0.2", + "schema-utils": "^4.0.0", + "selfsigned": "^2.1.1", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^5.3.1", + "ws": "^8.4.2" + }, + "dependencies": { + "ajv": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz", + "integrity": "sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + } + } + }, + "webpack-merge": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", + "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", + "requires": { + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" + } + }, + "webpack-node-externals": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-3.0.0.tgz", + "integrity": "sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==", + "dev": true + }, + "webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==" + }, + "webpack-subresource-integrity": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-5.1.0.tgz", + "integrity": "sha512-sacXoX+xd8r4WKsy9MvH/q/vBtEHr86cpImXwyg74pFIpERKt6FmB8cXpeuh0ZLgclOlHI4Wcll7+R5L02xk9Q==", + "dev": true, + "requires": { + "typed-assert": "^1.0.8" + } + }, + "webpackbar": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/webpackbar/-/webpackbar-5.0.2.tgz", + "integrity": "sha512-BmFJo7veBDgQzfWXl/wwYXr/VFus0614qZ8i9znqcl9fnEdiVkdbi0TedLQ6xAK92HZHDJ0QmyQ0fmuZPAgCYQ==", + "requires": { + "chalk": "^4.1.0", + "consola": "^2.15.3", + "pretty-time": "^1.1.0", + "std-env": "^3.0.1" + } + }, + "websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "requires": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + } + }, + "websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==" + }, + "whatwg-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "dev": true, + "requires": { + "iconv-lite": "0.6.3" + } + }, + "whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "dev": true + }, + "whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "dev": true, + "requires": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "requires": { + "isexe": "^2.0.0" + } + }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, + "which-typed-array": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", + "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.10" + } + }, + "why-is-node-running": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.2.2.tgz", + "integrity": "sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==", + "dev": true, + "requires": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + } + }, + "wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "requires": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "widest-line": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", + "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==", + "requires": { + "string-width": "^5.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==" + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + } + }, + "strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "requires": { + "ansi-regex": "^6.0.1" + } + } + } + }, + "wildcard": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", + "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==" + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==" + }, + "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==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + } + }, + "ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==" + }, + "xdg-basedir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==" + }, + "xml-js": { + "version": "1.6.11", + "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz", + "integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==", + "requires": { + "sax": "^1.2.4" + } + }, + "xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "dev": true + }, + "xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "dev": true, + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + } + }, + "xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true + }, + "xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" + }, + "xmlcreate": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", + "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==" + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" + }, + "yargs": { + "version": "17.6.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", + "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==", + "dev": true, + "requires": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + } + }, + "yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true + }, + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "requires": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "yazl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yazl/-/yazl-2.5.1.tgz", + "integrity": "sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==", + "dev": true, + "requires": { + "buffer-crc32": "~0.2.3" + } + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" + }, + "zip-stream": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz", + "integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==", + "requires": { + "archiver-utils": "^3.0.4", + "compress-commons": "^4.1.2", + "readable-stream": "^3.6.0" + }, + "dependencies": { + "archiver-utils": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-3.0.4.tgz", + "integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==", + "requires": { + "glob": "^7.2.3", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + } + } + } + }, + "zwitch": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-1.0.5.tgz", + "integrity": "sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==" + } + } } diff --git a/package-lock.json.license b/package-lock.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/package-lock.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/package.json b/package.json index c71bf8df..3f60332b 100644 --- a/package.json +++ b/package.json @@ -1,16 +1,114 @@ { + "name": "jayvee", + "version": "0.4.0", "scripts": { - "ts": "npx ts-node" + "nx": "nx", + "format": "nx format:write", + "build": "nx run-many --target build", + "build:prod": "nx run-many --target build --configuration=prod", + "lint": "nx run-many --target lint --max-warnings 0", + "test": "nx run-many --target test", + "generate": "nx run language-server:generate", + "example:cars": "nx run interpreter:run -d example/cars.jv -dg peek", + "example:cars-polars": "nx run interpreter:run -d example/cars.jv -dg peek --use-polars", + "example:testing": "nx run interpreter:run -d example/testing.jv -dg peek", + "example:testing-polars": "nx run interpreter:run -d example/testing.jv -dg peek --use-polars", + "example:one-block-polars": "nx run interpreter:run -d example/one-block.jv -dg peek --use-polars", + "example:gtfs": "nx run interpreter:run -d example/gtfs-static.jv -dg peek", + "example:gtfs-rt": "nx run interpreter:run -d example/gtfs-rt.jv -dg peek", + "example:workbooks": "nx run interpreter:run -d example/workbooks-xlsx.jv -dg peek", + "example:vehicles": "nx run interpreter:run -d -e DB_HOST=localhost -e DB_PORT=5432 -e DB_USERNAME=postgres -e DB_PASSWORD=postgres -e DB_DATABASE=postgres example/electric-vehicles.jv -dg peek" + }, + "private": true, + "dependencies": { + "@docusaurus/core": "2.4.1", + "@docusaurus/preset-classic": "2.4.1", + "@docusaurus/theme-mermaid": "2.4.1", + "@mdx-js/react": "^1.6.22", + "assert": "^2.0.0", + "chalk": "^4.1.2", + "clsx": "^1.2.1", + "commander": "^8.0.0", + "exceljs": "^4.3.0", + "fast-csv": "^4.3.6", + "follow-redirects": "^1.15.2", + "fp-ts": "^2.16.5", + "gtfs-realtime-bindings": "^1.1.1", + "jszip": "^3.10.1", + "langium": "^3.0.0", + "mime-types": "^2.1.35", + "monaco-editor": "^0.34.1", + "monaco-languageclient": "^4.0.3", + "nodejs-polars": "^0.11.0", + "pg": "^8.8.0", + "prism-react-renderer": "^1.3.5", + "react": "18.2.0", + "react-dom": "18.2.0", + "sqlite3": "^5.1.5", + "tslib": "^2.3.0", + "vscode-languageclient": "^9.0.1", + "vscode-languageserver": "^9.0.1", + "vscode-languageserver-protocol": "^3.17.2", + "vscode-uri": "^3.0.8" }, "devDependencies": { - "@types/node": "^20.12.7", - "typescript": "^5.4.5" + "@babel/core": "^7.14.5", + "@babel/preset-react": "^7.14.5", + "@docusaurus/module-type-aliases": "2.4.1", + "@jvalue/eslint-config-jvalue": "^1.3.0", + "@nx-plus/docusaurus": "^15.0.0-rc.0", + "@nx/esbuild": "18.0.1", + "@nx/eslint": "18.0.1", + "@nx/eslint-plugin": "18.0.1", + "@nx/js": "18.3.4", + "@nx/node": "18.0.1", + "@nx/react": "18.0.1", + "@nx/rollup": "18.0.1", + "@nx/vite": "^18.3.4", + "@nx/web": "18.3.4", + "@nx/webpack": "18.0.1", + "@nx/workspace": "18.0.1", + "@swc-node/register": "~1.8.0", + "@swc/core": "~1.3.85", + "@swc/helpers": "~0.5.2", + "@types/follow-redirects": "^1.14.1", + "@types/mime-types": "^2.1.1", + "@types/node": "18.19.11", + "@types/pg": "^8.6.5", + "@types/react": "18.2.24", + "@types/react-dom": "18.2.9", + "@types/vscode": "^1.56.0", + "@typescript-eslint/eslint-plugin": "6.20.0", + "@typescript-eslint/parser": "6.20.0", + "@vitest/coverage-v8": "^1.0.4", + "@vitest/ui": "^1.3.1", + "@vscode/vsce": "^2.19.0", + "esbuild": "^0.19.2", + "eslint": "^8.48.0", + "eslint-config-prettier": "9.1.0", + "eslint-config-react-app": "^7.0.1", + "eslint-plugin-import": "2.27.5", + "eslint-plugin-prettier": "^4.2.1", + "eslint-plugin-react": "7.32.2", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-unicorn": "^52.0.0", + "eslint-plugin-vitest": "^0.5.4", + "jest-environment-jsdom": "^29.7.0", + "langium-cli": "^3.0.0", + "nock": "13.3.1", + "nx": "18.0.1", + "prettier": "^2.8.7", + "ts-node": "10.9.1", + "typescript": "^5.0.4", + "vite": "~5.0.0", + "vitest": "^1.3.1" }, - "dependencies": { - "@types/sqlite3": "^3.1.11", - "node-sqlite": "^0.0.2-security", - "nodejs-polars": "^0.10.0", - "sqlite3": "^5.1.7", - "ts-node": "^10.9.2" + "overrides": { + "remark-parse@8.0.3": { + "trim": "^0.0.3" + }, + "package-json@6.5.0": { + "got": "^11.8.5" + } } } diff --git a/package.json.license b/package.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/package.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/rfc/0000-rfc-template.md b/rfc/0000-rfc-template.md new file mode 100644 index 00000000..ccd3ed46 --- /dev/null +++ b/rfc/0000-rfc-template.md @@ -0,0 +1,50 @@ +<!-- +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only +--> + +# RFC 0000: RFC Template <!-- TODO: adapt title --> + +| | | +|---|---| +| Feature Tag | `your-feature-name` | <!-- TODO: choose a unique and declarative feature name --> +| Status | `DRAFT` | <!-- Possible values: DRAFT, DISCUSSION, ACCEPTED, REJECTED --> +| Responsible | `github-tag` | <!-- TODO: assign yourself as main driver of this RFC --> +<!-- + Status Overview: + - DRAFT: The RFC is not ready for a review and currently under change. Feel free to already ask for feedback on the structure and contents at this stage. + - DISCUSSION: The RFC is open for discussion. Usually, we open a PR to trigger discussions. + - ACCEPTED: The RFC was accepted. Create issues to prepare implementation of the RFC. + - REJECTED: The RFC was rejected. If another revision emerges, switch to status DRAFT. +--> + +## Summary + +<!-- TODO: Write a 1-3 sentence summary what this RFC is about. --> + +## Motivation + +<!-- TODO: Describe the challenge the RFC addresses. --> + +<!-- TODO: (optional) Introduce an exemplary data set that benefits from this RFC (used later in explanation). --> + +## Explanation + +<!-- + TODO: Explain the details of the RFC. + If the RFC contains more than a single cohesive aspect, structure this section accordingly. + Make sure to provide realistic modelling examples on the example data set introduced above. +--> + +## Drawbacks + +<!-- TODO: (optional) Discuss the drawbacks of the proposed design. --> + +## Alternatives + +<!-- TODO: (optional) Point out alternatives to the design or parts of the design. --> + +## Possible Future Changes/Enhancements + +<!-- TODO: (optional) Point out what changes or enhancements you see in the future to the proposed concepts. --> diff --git a/rfc/0001-cell-ranges/README.md b/rfc/0001-cell-ranges/README.md new file mode 100644 index 00000000..aa193d7e --- /dev/null +++ b/rfc/0001-cell-ranges/README.md @@ -0,0 +1,200 @@ +<!-- +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only +--> + +- Feature Name: `cell-ranges` +- Status: `ACCEPTED` + +# Summary + +Enable reshaping messy 2-dimensional data into a processable structure. + +This is done by transforming sheets using different blocks. +[Cell ranges](./0003-cell-range-syntax.md) are used to configure such transformations. +In order to interpret the sheet as a table, +the aim is to form columns and optionally a header row +(similar to the usual structure of CSV files). + +Note that the actual conversion from a sheet to a table is part of an upcoming RFC. +This RFC focuses on the transformation of sheets to a desired structure. + +# Motivation + +Some 2-dimensional open data is badly structured. +For example, see this [CSV file from mobilithek.info](https://mobilithek.info/offers/-655945265921899037) +which does not follow the usual CSV pattern (topmost row as header, followed by the actual data) +but instead has metadata on the top and bottom of the file as well as two header rows +that provide no names for the first three columns. + +Excerpt of that example, featuring the top and bottom lines as well as the first line containing actual data: + +``` +GENESIS-Tabelle: 46251-0021 +Personenkraftwagen: Kreise, Stichtag, Kraftstoffarten,;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +Emissionsgruppen;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +Statistik des Kraftfahrzeug- und Anhängerbestandes;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +Personenkraftwagen (Anzahl);;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;Benzin;Benzin;Benzin;Benzin;Benzin;Benzin;Benzin;Benzin;Benzin;Benzin;Diesel;Diesel;Diesel;Diesel;Diesel;Diesel;Diesel;Diesel;Diesel;Diesel;Gas;Gas;Gas;Gas;Gas;Gas;Gas;Gas;Gas;Gas;Elektro;Elektro;Elektro;Elektro;Elektro;Elektro;Elektro;Elektro;Elektro;Elektro;Hybrid (ohne Plug-in);Hybrid (ohne Plug-in);Hybrid (ohne Plug-in);Hybrid (ohne Plug-in);Hybrid (ohne Plug-in);Hybrid (ohne Plug-in);Hybrid (ohne Plug-in);Hybrid (ohne Plug-in);Hybrid (ohne Plug-in);Hybrid (ohne Plug-in);Plug-in-Hybrid;Plug-in-Hybrid;Plug-in-Hybrid;Plug-in-Hybrid;Plug-in-Hybrid;Plug-in-Hybrid;Plug-in-Hybrid;Plug-in-Hybrid;Plug-in-Hybrid;Plug-in-Hybrid;Sonstige Kraftstoffarten;Sonstige Kraftstoffarten;Sonstige Kraftstoffarten;Sonstige Kraftstoffarten;Sonstige Kraftstoffarten;Sonstige Kraftstoffarten;Sonstige Kraftstoffarten;Sonstige Kraftstoffarten;Sonstige Kraftstoffarten;Sonstige Kraftstoffarten;Insgesamt;Insgesamt;Insgesamt;Insgesamt;Insgesamt;Insgesamt;Insgesamt;Insgesamt;Insgesamt;Insgesamt +;;;Euro 1;Euro 2;Euro 3;Euro 4;Euro 5;Euro 6 (ohne 6d und 6d-temp);Euro 6d-temp;Euro 6d;Sonstige;Insgesamt;Euro 1;Euro 2;Euro 3;Euro 4;Euro 5;Euro 6 (ohne 6d und 6d-temp);Euro 6d-temp;Euro 6d;Sonstige;Insgesamt;Euro 1;Euro 2;Euro 3;Euro 4;Euro 5;Euro 6 (ohne 6d und 6d-temp);Euro 6d-temp;Euro 6d;Sonstige;Insgesamt;Euro 1;Euro 2;Euro 3;Euro 4;Euro 5;Euro 6 (ohne 6d und 6d-temp);Euro 6d-temp;Euro 6d;Sonstige;Insgesamt;Euro 1;Euro 2;Euro 3;Euro 4;Euro 5;Euro 6 (ohne 6d und 6d-temp);Euro 6d-temp;Euro 6d;Sonstige;Insgesamt;Euro 1;Euro 2;Euro 3;Euro 4;Euro 5;Euro 6 (ohne 6d und 6d-temp);Euro 6d-temp;Euro 6d;Sonstige;Insgesamt;Euro 1;Euro 2;Euro 3;Euro 4;Euro 5;Euro 6 (ohne 6d und 6d-temp);Euro 6d-temp;Euro 6d;Sonstige;Insgesamt;Euro 1;Euro 2;Euro 3;Euro 4;Euro 5;Euro 6 (ohne 6d und 6d-temp);Euro 6d-temp;Euro 6d;Sonstige;Insgesamt +01.01.2022;01001;Flensburg, kreisfreie Stadt;259;1190;1208;7640;5700;6173;3098;1001;540;26809;86;583;1280;2308;4858;4305;1961;631;218;16230;1;36;37;115;31;20;11;8;3;262;-;-;-;-;-;-;-;-;741;741;-;-;-;17;64;128;300;394;-;903;-;-;-;-;8;41;120;357;-;526;-;-;-;8;6;-;-;-;3;17;346;1809;2525;10088;10667;10667;5490;2391;1505;45488 +__________ +Quelle: Kraftfahrt-Bundesamt, Flensburg +© Statistisches Bundesamt (Destatis), 2023 +Stand: 03.01.2023 / 22:01:12 +``` + +In order to bring such data into a better structure, +we need to be able to flexibly reshape such data and to omit parts we consider uninteresting. + +# Explanation + +## Desired structures + +For a conversion of sheets into database-conform tables, +sheets shall be transformed into one of the following two structures before interpreting them as a table: + +### Plain columns + +<img src="https://user-images.githubusercontent.com/51856713/215997813-a193e5b2-3f13-4a0a-9fda-2db00b08be93.png" width="500px"> + +Each column in the sheet represents a column in the database table, +and each row represents a record in the database table. +Note that the columns have no names so far. + +### Columns with topmost header row + +<img src="https://user-images.githubusercontent.com/51856713/215997925-b7396cd1-6716-42d9-85b4-dae9c9053d9e.png" width="500px"> + +The topmost row contains the names of the columns (e.g. `A1` contains the name for the first column). +After excluding the topmost row, each column in the sheet represents a column in the database table, +and each row represents a record in the database table. + +## Sheet transformation blocks + +The following sections present block types that are used to flexibly restructure sheets. +The goal is to enable transforming sheets into a desired structure +(see the corresponding [section above](#desired-structures)). + +Each of the following block types takes a `Sheet` as input and produces a `Sheet` as output. + +### `ColumnDeleter` / `RowDeleter` + +Can be used to delete entire columns or rows of a sheet. +The attribute `delete` specifies the columns / rows that shall be deleted. +Only cell ranges that select entire columns / rows are allowed. + +#### Example + +##### Delete the single column B + +```jayvee +block MyColumnDeleter oftype ColumnDeleter { + delete: [column B]; +} +``` + +![image](https://user-images.githubusercontent.com/51856713/216016548-5172d609-e31f-416a-969d-3469d1b3fbae.png) + +##### Delete the columns A and C + +```jayvee +block MyColumnDeleter oftype ColumnDeleter { + delete: [column A, column C]; +} +``` + +![image](https://user-images.githubusercontent.com/51856713/216016612-226a07a2-aa7e-4b95-982b-fcf36cb25c00.png) + +##### Delete the single row 2 + +```jayvee +block MyRowDeleter oftype RowDeleter { + delete: [row 2]; +} +``` + +![image](https://user-images.githubusercontent.com/51856713/216016668-5806e7c4-5eb0-4aac-93aa-b6c820ef10fc.png) + +##### Delete the rows 1 and 3 + +```jayvee +block MyRowDeleter oftype RowDeleter { + delete: [row 1, row 3]; +} +``` + +![image](https://user-images.githubusercontent.com/51856713/216016728-134057d6-f934-4918-a4b9-71f1bbed91cb.png) + +### `CellRangeSelector` + +Enables extracting a sub-sheet from a given sheet. +The desired sub-sheet is selected via the attribute `select` using an arbitrary cell range. + +Note that this can be considered a convenience block type +because the same transformation can be achieved by multiple instances of `ColumnDeleter` / `RowDeleter`. + +#### Example + +```jayvee +block MyCellRangeSelector oftype CellRangeSelector { + select: range B1:C2; +} +``` + +![image](https://user-images.githubusercontent.com/51856713/216016857-023c9d04-7d9c-4f25-8252-3a0c81fc58a5.png) + +### `CellWriter` + +Used for overriding the textual content of a particular cell. +The attribute `at` specifies the affected cell and `write` specifies the new content for the cell. + +#### Example + +Writing `some text` into cell `A2`: + +```jayvee +block MyCellWriter oftype CellWriter { + at: cell A2; + write: "some text"; +} +``` + +## Exemplary restructuring of the [mobilithek example](#motivation) from above + +```jayvee +block DataSelector oftype CellRangeSelector { + select: range A8:*483; +} + +pipe { + from: DataSelector; + to: IdColumnDeleter; +} + +block IdColumnDeleter oftype ColumnDeleter { + delete: [column B]; +} +``` + +Note that there is currently no way to merge the two header rows into one. +Thus, in the example code above, the header rows are entirely deleted. +The resulting sheet has the [column structure without a header row](plain-columns). + +# Future possibilities + +- A graphical sheet editor where each action in the editor generates an according transformation block + +# Drawbacks + +- Sheets may require multiple subsequent transformations, possibly making the pipeline hard to understand + - Esp. many cell writes require as many subsequent `CellWriter` blocks +- Altering indexes of rows / columns may be confusing for users + - E.g. when deleting column `A`, the previous column `B` becomes the new column `A` + - Could be diminished by having a graphical preview of the transformed sheet + +# Alternatives + +- Introduce different sheet transformation blocks +- Don't transform sheets, use previously proposed layout concept instead: https://github.com/jvalue/jayvee/pull/114 diff --git a/rfc/0002-mobility-extension/README.md b/rfc/0002-mobility-extension/README.md new file mode 100644 index 00000000..e51179bd --- /dev/null +++ b/rfc/0002-mobility-extension/README.md @@ -0,0 +1,152 @@ +<!-- +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only +--> + +| | | +|---|---| +| Feature Tag | `mobility-extension` | +| Status | `ACCEPTED` | <!-- Possible values: DRAFT, DISCUSSION, ACCEPTED, REJECTED --> +| Responsible | `@schlingling` | +| Implemented via | [#123](https://github.com/jvalue/jayvee/issues/123) | + +Disclaimer: This RFC is part of my master-thesis "Archiving open transport data using the JValue tooling ecosystem" supervised by @rhazn. + +# Summary +This RFC enables a pipeline extracting, validating and loading GTFS-data (part of domain mobility-data) by providing an GTFS-endpoint under consideration of the [GTFS-specification](https://developers.google.com/transit/gtfs/reference). For that reason some changes and extensions of Jayvee have to be made. The overall goal of this RFC is processing GTFS-data with a minimum of changes/extensions in Jayvee. + +# Motivation +GTFS has gained widespread popularity over the past decade as an open-source industry standard for describing and publishing fixed- and dynamic route transit operations. It is a data standard that defines how public transit agencies can provide schedule information to developers. It is used by agencies around the world to publish their transit data in a common format, allowing developers to create applications that can access and use this data. GTFS data includes information about stops, routes, and schedules for buses, trains, and other forms of public transportation. GTFS-data is provided by an endpoint, which publishes a zip-file, consisting of a collection of comma-separated-values in plain text files. A example of a gtfs-zip-file could result in this datamodel (this visualization just includes required dimensions). +![Visualization of a Gtfs data model](./visualization-gtfs-datamodel.png) + +# Explanation +The following picture is a visualization of the corresponding [example.jv](example.jv)-file. A GTFS-pipeline follows the overall pipeline pattern, consisting of an Extractor, some Interpreters, some Validators and finally a Loader to a sink (in our case all gtfs-csv-files are loaded into a SQLite database, each csv-file into its corresponding table). The individual GTFS files are picked out using their filename and further processed independently in parallel using block types that already exist (or at least in a similar form). In the image, there are three such parallel processing steps as an example. In practice, there would be one for each GTFS file in the ZIP archive. In case a file are not present, the further processing of that file is aborted and hence no table is created from that file. At the end, each successfully created table is loaded into the same SQLiteSink. + +![Concept of a Gtfs Pipeline](./visualization-gtfs-pipeline-filepicker.png) + +The red block types need to be created from scratch whereas the blue block types are either already present or only require minor changes (this classification is also reflected in the following chapter titles). + +Jayvee needs to be extented by following parts to be able to process GTFS-data: +* New `io-datatypes` called `File` and `FileSystem` +* New blocktypes `HTTPExtractor`, `ArchiveInterpreter` and `FilePicker` +* The `io-datatype` `Table` needs to store its name to be able to handle multiple tables as input +* An abort-mechanism must be implemented, for that we need a new io-type `None` to abort, if a precessor outputs `None` + +Each of the following subchapters explains the idea behind. + +## New io-datatypes + +### io-datatype File *(Requires implementation from scratch)* +A File datatype could look like this and should be added to `io-datatypes.ts`. +``` +export interface File { + name: string // The name of the file, excluding its file extension + + extension: string //The file extension + + filetype: string //The MIME type of the file taken from the Content-Type header (for HTTP requests only) Otherwise inferred from the file extension, default application/octet-stream for unknown or missing file extensions + + content: ArrayBuffer //The content of the file as a ArrayBuffer +} +``` +### io-datatype FileSystem *(Requires implementation from scratch)* +A FileSystem could look like this and should be added to `io-datatypes.ts`. Provides generic methods for navigating in the file system using paths and for accessing files. In order to implement the interface, we create a class which provides the attributes / methods demanded by the interface. +``` +export interface FileSystem { + //Methods as needed +} +``` + +### io-datatype None *(Requires implementation from scratch)* +A None type could look like this and should be added to `io-datatypes.ts`. If a block output emits a None value, downstream blocks are not executed for that value. +``` +export interface None { + //Methods as needed +} +``` + +### io-datatype Table *(Requires minor change)* +The io-datatype `Table` should be adapted, to store its name to be able to handle multiple tables as input later in an DB-Loader. This leads also to a minor change in the LayoutValidator and the example to process the additional attribute `tableName`. +``` +export interface Table { + tableName: string; + columnNames: string[]; + columnTypes: Array<AbstractDataType | undefined>; + data: string[][]; +} +``` + +### Change of folderstructure +Since we are introducing multiple new io-datatypes and some implemenations of them, we move the file `io-datatype.ts` to a new folder, holding all types and implemenations. + +## New Block Types +### 1) HttpExtractor *(Requires implementation from scratch)* +Input: void, Output: File + +A HttpExtractors gets an Url, sends an HTTP-GET-REQUEST to that URL and outputs the response as `File`. This block can be used for getting any kind of data via a HTTP-Endpoint. It should be implemented in the std-extension. +``` +block MyHttpExtractor oftype HttpExtractor { + url: "https://www.data.gouv.fr/fr/datasets/r/c4d9326f-9f41-4dfb-9746-31bc97a31fc6"; +} +``` + +### 2) ArchiveInterpreter *(Requires implementation from scratch)* +Input: File, Output: FileSystem + +A ArchiveInterpreter gets a File, and initializes an FileSystem ontop of the file (open filestream etc.). Provides generic methods for navigating in the file system using paths and for accessing files. As it is not clear, what the file contains. It should be implemented in the std-extension. The ArchiveInterpreter needs to be able to instantiate a FileSystem instance in order to output it as a result. +``` +block ZipArchiveInterpreter oftype ArchiveInterpreter{ + archiveType: string //now only accepting the string "zip" +} +``` + +### 3) FilePicker *(Requires implementation from scratch)* +Input: FileSystem, Output: File + +A FilePicker gets an FileSystem, navigates to the file, and initializes an file via the path. The FilePicker needs to work with methods provided by a FileSystem instance in order to read the file according to the provided path. +``` +block MyFilePicker oftype FilePicker{ + path: string // Absolute path to file (from the root folder) eg. /agency.txt +} +``` + +### 4) CSVInterpreter *(Requires minor change)* +Input: File, Output: Sheet + +In the package `tabular` a `CSVFileExtractor` is already implemented, which loads a CSV from an URL and outputs a Sheet. We need to rewrite the existing example pipelines (gas and cars), to use the new `HTTPExtractor` as well, then we just need one `CSVFileInterpreter` and now longer an `CSVFileExtractor`. + +### 5) LayoutValidator and Layouts *(Requires minor change)* +The following description is out of scope for this RFC, will be considered in future but is important for understanding the gtfs-specification: +* Some columns in GTFS-csv-files are optional and conditional optional +* For an implementation of an optional mechanism, we need to present the optional columns with their datatype, e.g. saying their datatype is text or undefined. +* So, we'd need an undefined datatype in Jayvee and a way to combine these types using an or-expression (see chapter datatypes). + +For a first draft, we only consider required columns and reuse the existing language features for that. + +``` +layout agencyLayout { + header row 1: text; + column agency_id: text; //Conditional columns are considered as required + column agency_name: text; + column agency_url: text; + column agency_timezone: text; + } +``` + +A vision is, that the GTFS-pipeline later on processes a list of GTFS-Endpoints. Because every endpoint has at least the required-columns, we need to have the optional-mechanism in our layout. + +In a GTFS-Validator, some conditional (aka logical) checks could possibly be applied during the validation (not just a static header/datatype validaton). This could be done by checking required columns. If in future an conditional required is not longer considered as required, we could also implement conditional logical validation. An example for that is the columns agency_id in table agencies. The specification states, that the agency_id is optional, when the whole dataset just contains data from one agency. + +**IMPORTANT:** In the GTFS specifiation, the order of the columns is not defined, so we need to access the columns by their names, not their index as every GTFS-endpoint could possibly have a different order!! + +### 6) SQLiteSink *(Requires minor change)* +Input: Table, Output: void +This Block needs to be adapted, to handle multiple Inputs. As the parallel processing of the differnt files does not depend on each other, for an inital demonstration if the PoC we use each own SQLiteSinks without the effort of modifying the execution logic (here we have to change the SQLite sink, to not recreate the DB each call). We also change the SQLiteSink to receive the table name via the io-datatype `Table` itself. +``` +block GtfsLoader oftype SQLiteTablesLoader { + file: "./gtfs.db"; + } +``` + + diff --git a/rfc/0002-mobility-extension/example.jv b/rfc/0002-mobility-extension/example.jv new file mode 100644 index 00000000..0be0dee9 --- /dev/null +++ b/rfc/0002-mobility-extension/example.jv @@ -0,0 +1,362 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline GtfsPipeline { + + block MyHttpExtractor oftype HttpExtractor { + url: "https://developers.google.com/static/transit/gtfs/examples/sample-feed.zip"; + } + + block ZipArchiveInterpreter oftype ArchiveInterpreter{ + archiveType: "zip"; + } + + block AgencyFilePicker oftype FilePicker{ + path: "/agency.txt"; + } + + block CalendarDatesFilePicker oftype FilePicker{ + path: "/calendar_dates.txt"; + } + + block CalendarFilePicker oftype FilePicker{ + path: "/calendar.txt"; + } + + block FareAttributesFilePicker oftype FilePicker{ + path: "/fare_attributes.txt"; + } + + block FareRulesFilePicker oftype FilePicker{ + path: "/fare_rules.txt"; + } + + block FrequenciesFilePicker oftype FilePicker{ + path: "/frequencies.txt"; + } + + block RoutesFilePicker oftype FilePicker{ + path: "/routes.txt"; + } + + block ShapesFilePicker oftype FilePicker{ + path: "/shapes.txt"; + } + + block StopTimesFilePicker oftype FilePicker{ + path: "/stop_times.txt"; + } + + block StopsFilePicker oftype FilePicker{ + path: "/stops.txt"; + } + + block TripsFilePicker oftype FilePicker{ + path: "/trips.txt"; + } + + block AgencyCSVInterpreter oftype CSVInterpreter{ + + } + + block CalendarDatesCSVInterpreter oftype CSVInterpreter{ + + } + + block CalendarCSVInterpreter oftype CSVInterpreter{ + + } + + block FareAttributesCSVInterpreter oftype CSVInterpreter{ + + } + + block FareRulesCSVInterpreter oftype CSVInterpreter{ + + } + + block FrequenciesCSVInterpreter oftype CSVInterpreter{ + + } + + block RoutesCSVInterpreter oftype CSVInterpreter{ + + } + + block ShapesCSVInterpreter oftype CSVInterpreter{ + + } + + block StopTimesCSVInterpreter oftype CSVInterpreter{ + + } + + block StopsCSVInterpreter oftype CSVInterpreter{ + + } + + + block TripsCSVInterpreter oftype CSVInterpreter{ + + } + + block AgencyTableInterpreter oftype TableInterpreter { + header: true; + columns:[ + "agency_id" typed text, //Conditional columns are considered as required + "agency_name" typed text, + "agency_url" typed text, + "agency_timezone" typed text + ]; + } + + block CalendarDatesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "service_id" typed text, + "date" typed text, + "exception_type" typed text + ]; + } + + block CalendarTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "service_id" typed text, + "monday" typed text, + "tuesday" typed text, + "wednesday" typed text, + "thursday" typed text, + "friday" typed text, + "saturday" typed text, + "sunday" typed text, + "start_date" typed text, + "end_date" typed text + ]; + } + + block FareAttributesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "fare_id" typed text, + "price" typed text, + "currency_type" typed text, + "payment_method" typed text, + "transfers" typed text, + "transfer_duration" typed text + ]; + } + + block FareRulesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "fare_id" typed text, + "route_id" typed text, + "origin_id" typed text, + "destination_id" typed text, + "contains_id" typed text + ]; + } + + block FrequenciesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "trip_id" typed text, + "start_time" typed text, + "end_time" typed text, + "headway_secs" typed text + ]; + } + + block RoutesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "route_id" typed text, + "agency_id" typed text, + "route_short_name" typed text, + "route_long_name" typed text, + "route_desc" typed text, + "route_type" typed text, + "route_url" typed text, + "route_color" typed text, + "route_text_color" typed text + ]; + } + + block ShapesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "shape_id" typed text, + "shape_pt_lat" typed text, + "shape_pt_lon" typed text, + "shape_pt_sequence" typed text, + "shape_dist_traveled" typed text + ]; + } + + block StopTimesTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "trip_id" typed text, + "arrival_time" typed text, + "departure_time" typed text, + "stop_id" typed text, + "stop_sequence" typed text, + "stop_headsign" typed text, + "pickup_type" typed text, + "drop_off_time" typed text, + "shape_dist_traveled" typed text + ]; + } + + block StopsTableInterpreter oftype TableInterpreter { + header: true; + columns:[ + "stop_id" typed text, + "stop_name" typed text, + "stop_desc" typed text, + "stop_lat" typed text, + "stop_lon" typed text, + "zone_id" typed text, + "stop_url" typed text + ]; + } + + + block TripsTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "route_id" typed text, + "service_id" typed text, + "trip_id" typed text, + "trip_headsign" typed text, + "direction_id" typed text, + "block_id" typed text, + "shape_id" typed text + ]; + } + + block AgencyLoader oftype SQLiteLoader { + table: "agency"; + file: "./mobility.sqlite"; + } + + block CalendarDatesLoader oftype SQLiteLoader { + table: "calendar_dates"; + file: "./mobility.sqlite"; + } + + block CalendarLoader oftype SQLiteLoader { + table: "calendar"; + file: "./mobility.sqlite"; + } + + block FareAttributesLoader oftype SQLiteLoader { + table: "fare_attributes"; + file: "./mobility.sqlite"; + } + + block FareRulesLoader oftype SQLiteLoader { + table: "fare_rules"; + file: "./mobility.sqlite"; + } + + block FrequenciesLoader oftype SQLiteLoader { + table: "frequencies"; + file: "./mobility.sqlite"; + } + + block RoutesLoader oftype SQLiteLoader { + table: "routes"; + file: "./mobility.sqlite"; + } + + block ShapesLoader oftype SQLiteLoader { + table: "shapes"; + file: "./mobility.sqlite"; + } + + block StopTimesLoader oftype SQLiteLoader { + table: "stop_times"; + file: "./mobility.sqlite"; + } + + block StopsLoader oftype SQLiteLoader { + table: "stops"; + file: "./mobility.sqlite"; + } + + block TripsLoader oftype SQLiteLoader { + table: "trips"; + file: "./mobility.sqlite"; + } + + + MyHttpExtractor + -> ZipArchiveInterpreter + -> AgencyFilePicker + -> AgencyCSVInterpreter + -> AgencyTableInterpreter + -> AgencyLoader; + + ZipArchiveInterpreter + -> CalendarDatesFilePicker + -> CalendarDatesCSVInterpreter + -> CalendarDatesTableInterpreter + -> CalendarDatesLoader; + + ZipArchiveInterpreter + -> CalendarFilePicker + -> CalendarCSVInterpreter + -> CalendarTableInterpreter + -> CalendarLoader; + ZipArchiveInterpreter + -> FareAttributesFilePicker + -> FareAttributesCSVInterpreter + -> FareAttributesTableInterpreter + -> FareAttributesLoader; + + ZipArchiveInterpreter + -> FareRulesFilePicker + -> FareRulesCSVInterpreter + -> FareRulesTableInterpreter + -> FareRulesLoader; + + ZipArchiveInterpreter + -> FrequenciesFilePicker + -> FrequenciesCSVInterpreter + -> FrequenciesTableInterpreter + -> FrequenciesLoader; + + ZipArchiveInterpreter + -> RoutesFilePicker + -> RoutesCSVInterpreter + -> RoutesTableInterpreter + -> RoutesLoader; + + ZipArchiveInterpreter + -> ShapesFilePicker + -> ShapesCSVInterpreter + -> ShapesTableInterpreter + -> ShapesLoader; + + ZipArchiveInterpreter + -> StopTimesFilePicker + -> StopTimesCSVInterpreter + -> StopTimesTableInterpreter + -> StopTimesLoader; + + ZipArchiveInterpreter + -> StopsFilePicker + -> StopsCSVInterpreter + -> StopsTableInterpreter + -> StopsLoader; + + ZipArchiveInterpreter + -> TripsFilePicker + -> TripsCSVInterpreter + -> TripsTableInterpreter + -> TripsLoader; + +} \ No newline at end of file diff --git a/rfc/0002-mobility-extension/visualization-gtfs-datamodel.png b/rfc/0002-mobility-extension/visualization-gtfs-datamodel.png new file mode 100644 index 00000000..46fdd70a Binary files /dev/null and b/rfc/0002-mobility-extension/visualization-gtfs-datamodel.png differ diff --git a/rfc/0002-mobility-extension/visualization-gtfs-datamodel.png.license b/rfc/0002-mobility-extension/visualization-gtfs-datamodel.png.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/rfc/0002-mobility-extension/visualization-gtfs-datamodel.png.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/rfc/0002-mobility-extension/visualization-gtfs-pipeline-filepicker.png b/rfc/0002-mobility-extension/visualization-gtfs-pipeline-filepicker.png new file mode 100644 index 00000000..44941f75 Binary files /dev/null and b/rfc/0002-mobility-extension/visualization-gtfs-pipeline-filepicker.png differ diff --git a/rfc/0002-mobility-extension/visualization-gtfs-pipeline-filepicker.png.license b/rfc/0002-mobility-extension/visualization-gtfs-pipeline-filepicker.png.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/rfc/0002-mobility-extension/visualization-gtfs-pipeline-filepicker.png.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/rfc/0003-cell-range-syntax/README.md b/rfc/0003-cell-range-syntax/README.md new file mode 100644 index 00000000..5e55557b --- /dev/null +++ b/rfc/0003-cell-range-syntax/README.md @@ -0,0 +1,116 @@ +<!-- +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only +--> + +- Feature Name: `cell-range-syntax` +- Status: `ACCEPTED` + +# Summary + +Syntax for selecting parts of 2-dimensional data. The selection may be a single cell, a 1-dimensional series of cells or a 2-dimensional subset of cells. + +The syntax is inspired by sheet software (i.e. Excel and Google Sheets) where columns are denoted by capital letters and rows by integer indices. + +# Motivation + +Sheet-like data may contain uninteresting sections we wish to omit and other sections we would like to keep and further process. In order to denote such ranges of interest, we need a dedicated syntax that is easy to learn and intuitive to use. + +We call such selections cell ranges and hereby propose their syntax. + +# Explanation + +The syntax of cell ranges follows the pattern `range <from>:<to>` where `<from>` and `<to>` refer to a particular cell. A particular cell is selected via its column and its row in the sheet. The `*` character can be used to denote the last column or row. + +There are also syntactic sugar versions for common cell ranges (i.e. selecting an entire row / column or a particular cell) by using the keywords `row`, `column` or `cell` instead of `range`. + +The following sections show some examples for different cell ranges: + +## Selecting an entire row + +```jayvee +// selecting the entire row 1 +range A1:*1 +``` + +sugars to + +```jayvee +// selecting the entire row 1 +row 1 +``` + +## Selecting an entire column + +```jayvee +// selecting the entire column A +range A1:A* +``` + +sugars to + +```jayvee +// selecting the entire column A +column A +``` + +## Selecting a specific cell + +```jayvee +// selecting the cell B2 +range B2:B2 +``` + +sugars to + +```jayvee +// selecting the cell B2 +cell B2 +``` + +## Selecting a 2-dimensional cell range + +```jayvee +// selecting the cells from A2 to B4 +range A2:B4 +``` + +## Selecting an entire table + +```jayvee +// selecting the entire table +range A1:** +``` + +## Selecting multiple adjacent columns / rows + +```jayvee +// selecting the two columns B and C +range B1:C* + +// selecting the three rows 4, 5 and 6 +range A4:*6 +``` + +## Selecting partial columns / rows + +```jayvee +// Selecting column B from row 2 onwards +range B2:B* + +// Selecting row 3 from column C onwards +range C3:*3 +``` + +# Drawbacks + +- Unable to select multiple non-adjacent cells / rows / columns +- Unable to select cells using regular patterns (e.g. selecting every second row) +- Unable to combine multiple cell ranges to form a new cell range +- Unable to conveniently select cells close to the last row / column (e.g. selecting the second last row) + +# Alternatives + +- Omit `*` when denoting the last row or column + - E.g. `A2:A` instead of `A2:A*` diff --git a/rfc/0004-table-interpretation/README.md b/rfc/0004-table-interpretation/README.md new file mode 100644 index 00000000..1e7d0c9a --- /dev/null +++ b/rfc/0004-table-interpretation/README.md @@ -0,0 +1,151 @@ +<!-- +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only +--> + +- Feature Name: `table-interpretation` +- Status: `ACCEPTED` + +# Summary + +The interpretation of sheets as tables shall be simplified and also work with sheets that don't contain a header row. +Therefore, a new block type `TableInterpreter`: `Sheet` ➔ `Table` is introduced which is meant to replace the +current `LayoutValidator` block type and the `layout` language concept. + +# Motivation + +Currently, a user needs to define a `layout` where columns are selected and given a type and a row is marked as header. +Sheets without header rows cannot be handled. The layout needs to be referenced in a `LayoutValidator` where the types +are validated and the resulting table is output. + +When assuming that sheets are present in a CSV-like structure (columns with an optional topmost header row), we are able +to simplify the overall table interpretation because it is clear how columns are arranged and where to find the column +names in the sheet. Even if a sheet exceptionally doesn't have such a proper +structure, [sheet transformation blocks](./0001-cell-ranges.md) can be used to restructure it accordingly. + +# Explanation + +Sheets that serve as input for `TableInterpreter` are expected to have one of two structures ( +either [plain columns](#sheet-with-plain-columns) or [columns with header](#sheet-with-header-row-and-columns)). Users +then are able to describe tables by stating whether a topmost header row is present or not and by assigning names and +types to each column. + +For specifying whether a header is present, the attribute `header` of `TableInterpreter` can be either set to `true` +or `false`. + +The individual columns of a table are specified in an attribute `columns` where column names are mapped to their type. +In case a header is present, the column names are mapped to the column with a corresponding header ( +see [this section](#sheet-with-header-row-and-columns) for further details). + +The following two sections show examples for using the `TableInterpreter` and further explain the semantics of the block +when used in different settings. + +## Sheet with plain columns + +One use case are sheets that have individual columns but no header row: + +<img src="https://user-images.githubusercontent.com/51856713/215997813-a193e5b2-3f13-4a0a-9fda-2db00b08be93.png" width="500px"> + +The following code snippet would be able to create a new table out of the sheet: + +```jayvee +block MyTableInterpreter oftype TableInterpreter { + header: false; + columns: [ + "col1" typed text, + "col2" typed integer, + "col3" typed boolean, + "col4" typed decimal, + ] +} +``` + +The attribute `header: false` indicates that the sheet has no topmost header row, so the column names are taken from the +provided names in the `columns` attribute. There, each entry describes a column by defining its name and its type. Note +that the order of columns matters, as the first entry maps to the first column `A`, the second to `B` and so on. + +## Sheet with header row and columns + +The other use case are sheets with a topmost header row which contains the name for each column: + +<img src="https://user-images.githubusercontent.com/51856713/215997925-b7396cd1-6716-42d9-85b4-dae9c9053d9e.png" width="500px"> + +The code snippet below would be able to interpret that sheet as a table: + +```jayvee +block MyTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "col1" typed text, + "col2" typed integer, + "col3" typed boolean, + "col4" typed decimal, + ] +} +``` + +The attribute `header: true` indicates that the sheet has a topmost header row, so the provided names in the `columns` +attribute are mapped to the names in the header. In case a provided name is not present in the header, that column is +omitted. Note that, as a consequence, the order of entries in `columns` does not matter in case there is a header. + +The following section presents a concrete example of how names in the header are mapped to column names and how missing +columns are handled. + +### GTFS example + +This example presents how a GTFS file could be processed using `TableInterpreter`. The specialty is that they contain +headers, but the order of columns is arbitrary and that some columns are considered optional and thus might not be +present. + +Consider the following sheet which represents a `trips.txt` GTFS +file ([source](https://developers.google.com/transit/gtfs/examples/gtfs-feed#tripstxt), [reference](https://developers.google.com/transit/gtfs/reference#tripstxt)): + +| route_id | service_id | trip_id | trip_headsign | block_id | +|:--------:|:----------:|:--------:|:-------------:|:--------:| +| **A** | **WE** | **AWE1** | **Downtown** | **1** | +| **A** | **WE** | **AWE2** | **Downtown** | **2** | + +The following Jayvee code converts the sheet into a table. Note that the order of entries in the `columns` attribute can +be arbitrary: + +```jayvee +block MyTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "trip_id" typed text, + "service_id" typed integer, + "route_id" typed text, + + // Optional columns: + "trip_headsign" typed text, + "trip_short_name" typed text, + "direction_id" typed integer, + "block_id" typed text, + "shape_id" typed text, + "wheelchair_accessible" typed integer, + "bikes_allowed": typed integer, + ] +} +``` + +Then this is what the resulting table looks like: + +| `text` <br> trip_id | `integer` <br> service_id | `text` <br> route_id | `text` <br> trip_headsign | `text` <br> block_id | +|:-------------------:|:-------------------------:|:--------------------:|:-------------------------:|:--------------------:| +| AWE1 | WE | A | Downtown | 1 | +| AWE2 | WE | A | Downtown | 2 | + +# Future possibilities + +- Ability to auto-generate `TableInterpreter` code by inferring columns from a sheet + +# Drawbacks + +- The `TableInterpreter` presumes a structure that might not be given +- Different semantics for sheets with and without header rows (e.g. order of columns may matter) + +# Alternatives + +- Split the `TableInterpreter` into two block types for handling sheets with and without header +- Use previous layout concept instead diff --git a/rfc/0005-pipes-syntactic-sugar/README.md b/rfc/0005-pipes-syntactic-sugar/README.md new file mode 100644 index 00000000..5f825cb3 --- /dev/null +++ b/rfc/0005-pipes-syntactic-sugar/README.md @@ -0,0 +1,158 @@ +<!-- +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only +--> + +# RFC 0005: Syntactic sugar for pipes + +| | | +| ----------- | ----------------------- | +| Feature Tag | `pipes-syntactic-sugar` | +| Status | `ACCEPTED` | +| Responsible | @rhazn | + +# Summary +Syntactic sugar to express pipes is suggested to support an overview of the whole pipeline. The added syntax is a shortened form of the existing approach: (`Block1 -> Block2`). + + +# Motivation +Pipes are modelled explicitly and separately of blocks. This theoretically allows the definition of a data pipeline in one place, by connecting previously desribed blocks. Shortened syntax would allow this overview to be more intuitively readable. + +# Explanation +To illustrate the syntactic sugar changes, the current cars data pipeline model will be used as running example. Some irrelevant column definitions have been removed to reduce the number of lines. + +## Running example: Current Cars Pipeline +```jayvee +pipeline CarsPipeline { + block CarsExtractor oftype CSVFileExtractor { + url: "https://gist.githubusercontent.com/noamross/e5e10b/raw/b9/cars.csv"; + } + + pipe { + from: CarsExtractor; + to: CarColumnNameWriter; + } + + block CarColumnNameWriter oftype CellWriter { + at: cell A1; + write: "name"; + } + + pipe { + from: CarColumnNameWriter; + to: CarsValidator; + } + + layout CarsLayout { + header row 1: text; + + column A: text; + column B: decimal; + column C: integer; + } + + block CarsValidator oftype LayoutValidator { + validationLayout: CarsLayout; + } + + pipe { + from: CarsValidator; + to: CarsLoader; + } + + block CarsLoader oftype SQLiteLoader { + table: "Cars"; + file: "./cars.db"; + } +} +``` + +## Suggestion: Arrow syntax +The arrow syntax shortens the definition of a pipe and allows multiple pipes to be chained. It enables users to define the structure of their pipeline from previously defined blocks. The syntax is short and intuitively looks like a pipeline. + +In the most basic form, any definition of a pipe is just replaced with an arrow `->` between the `from` and `to` attributes: + +```jayvee +pipe { + from: CarsExtractor; + to: CarColumnNameWriter; +} +``` + +is equivalent to + +```jayvee +CarsExtractor -> CarColumnNameWriter; +``` + +Additionally, pipelines defined with the arrow syntax can be chained. Consider any `->` as a pipeline definition, parse the left side of the arrow expression to be the `from` attribute and the right side the `to` attribute. + +```jayvee +pipe { + from: CarsExtractor; + to: CarColumnNameWriter; +} + +pipe { + from: CarColumnNameWriter; + to: CarsValidator; +} +``` + +is equivalent to + +```jayvee +CarsExtractor + -> CarColumnNameWriter + -> CarsValidator; +``` + +This syntax shortens the running example to the following, making it much more easy to read how the resulting pipeline will look. + +```jayvee +pipeline CarsPipeline { + block CarsExtractor oftype CSVFileExtractor { + url: "https://gist.githubusercontent.com/noamross/e5e10b/raw/b9/cars.csv"; + } + + block CarColumnNameWriter oftype CellWriter { + at: cell A1; + write: "name"; + } + + layout CarsLayout { + header row 1: text; + + column A: text; + column B: decimal; + column C: integer; + } + + block CarsValidator oftype LayoutValidator { + validationLayout: CarsLayout; + } + + block CarsLoader oftype SQLiteLoader { + table: "Cars"; + file: "./cars.db"; + } + + CarsExtractor + -> CarColumnNameWriter + -> CarsValidator + -> CarsLoader; +} +``` + +# Drawbacks +- Adding syntactic sugar adds new concepts for users to learn + +# Alternatives +- Use different arrow syntax `=>`, `-->` ... +- Move pipeline definitions into blocks instead of to a separate part of a jv model + - Would allow IDEs to offer hints based on inputs, e.g., by suggesting column names that have been defined in an input block for tabular data + - Instead of having an overview of the pipeline structure in one place, would move more context to the specific block a user is defining which might be more intuitive for simple pipelines + +# Possible Future Changes/Enhancements +- Introduce syntax for blocks without a name and allow to use them as `from` and `to` attributes for pipes in arrow syntax \ No newline at end of file diff --git a/rfc/0006-gtfs-rt-support/README.md b/rfc/0006-gtfs-rt-support/README.md new file mode 100644 index 00000000..c742d1f1 --- /dev/null +++ b/rfc/0006-gtfs-rt-support/README.md @@ -0,0 +1,90 @@ +<!-- +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only +--> + +# RFC 0006: GTFS-RT Support + +| | | +|---|---| +| Feature Tag | `gtfs-rt-support` | +| Status | `ACCEPTED` | <!-- Possible values: DRAFT, DISCUSSION, ACCEPTED, REJECTED --> +| Responsible | `@schlingling` | +| Implemented via | [#219](https://github.com/jvalue/jayvee/issues/219) | +<!-- + Status Overview: + - DRAFT: The RFC is not ready for a review and currently under change. Feel free to already ask for feedback on the structure and contents at this stage. + - DISCUSSION: The RFC is open for discussion. Usually, we open a PR to trigger discussions. + - ACCEPTED: The RFC was accepted. Create issues to prepare implementation of the RFC. + - REJECTED: The RFC was rejected. If another revision emerges, switch to status DRAFT. +--> +Disclaimer: This RFC is part of my master-thesis "Archiving open transport data using the JValue tooling ecosystem" supervised by @rhazn. +## Summary +Introduces support for [GTFS-RT](https://developers.google.com/transit/gtfs-realtime) (realtime) endpoints and extends therefore functionality of [0002-mobility-extension](https://github.com/jvalue/jayvee/tree/main/rfc/0002-mobility-extension). With this RFC, Jayvee can then process pipelines, which are extracting static public transportation schedules and associated geographic information and on top realtime updates about associated fleets like delays, cancellations, vehicle positions, etc. + +## Motivation +When it comes to nearly realtime updates, Google introduced an additional specification GTFS-RT on top of GTFS. This specification provides real-time up-dates to transit schedules and locations. It allows developers to access real-time +information about the location and status of vehicles, as well as any disruptions +or delays in service. GTFS-RT data is typically provided in +shape of streaming data feeds that are updated in real-time as events occur. +This realtime-feed always needs its corresponding static feed, which defines the +schedule and dimensions like `agency.txt` or `routes.txt` around live updates. The realtime specification can be divided into three types +of additional information, which enriches the static GTFS-feed: +* Trip updates - cancellations, delays and changed routes +* Service alerts - unforeseen events with impact on the transportation net- +work +* Vehicle positions - realtime information on vehicles position in coordinates + +![Visualization of a GTFS file collection including GTFS-RT](./visualization-gtfs-file-collection-including-gtfs-rt.png) + +## Explanation +In contrast to static GTFS, which only changes manually, when new schedules are +released, realtime feeds require a frequent update rate (in the range of seconds), +since live locations are played out. For this reason, it is specified that GTFS-RT is +streamed using the protocol buffer format, which corresponds to a very efficient +binary representation of the data. As a result, consuming +and processing a GTFS-RT-feed needs an additional encoding stage to convert +the messages to human readable plain text. The `gtfs-realtime.proto` textfile is used for parsing the protocol buffer into an JSON-like representation. + +A simple GTFS-RT-Pipeline is shown in the picture below. +![Simple GTFS-RT pipeline](simple-gtfs-rt-pipeline.png) + +The red block types need to be created from scratch whereas the green block types are either already present or only require minor changes (this classification is also reflected in the following chapter titles). + +Since realtime feeds require a frequent update rate, we enable a periodical load from GTFS **and** GTFS-RT data by following concept already discussed with @rhazn: + +1. One pipeline containing both, GTFS and GTFS-RT sections. +2. Periodical execution of the pipeline. +3. An additional attribute for SQLite-sink indicates whether tables should be dropped before load starts. GTFS-tables are dropped every run, GTFS-RT-tables not, which leads to the dataset visualized above in the Output-SQLite-Database containing the static GTFS-information as well as the incrementally growing RT-tables. + +This concept results in following pipeline. + +![Example pipeline for GTFS and GTFS-RT](./gtfs-and-gtfs-rt-pipeline.png) + +## Block Types +### 1) GtfsRTInterpreter *(Requires implementation from scratch)* +Input: File, Output: Sheet + +A GtfsRTInterpreter gets an entity ("vehicle", "trip_update" or "alert") to process from the incoming protobuf-file, decodes the protobuf-file and outputs the entity as a sheet. In a first step, just required columns (defined in `gtfs-realtime.proto`) are considered. As we dont have a dedicated mobility-extension folder in Jayvee this should be implemented in the std-extension (already discussed with @rhazn). +``` +block MyGtfsRTInterpreter oftype GtfsRTInterpreter { + entity: "vehiclePosition"; // TEXT: "vehiclePosition", "tripUpdate" or "alert" +} +``` +### 2) SQLiteSink *(Requires minor change)* +The SQLite sink needs an additional attribute indicating whether table should be dropped before load starts. Since blocks currently just support one input, a boolean data type for dropping a table is enough. +``` +block VehicleLoader oftype SQLiteLoader { + file: "./gtfs.db"; + dropTable: false // BOOLEAN +} +``` +## Drawbacks +The proposed concept is functional, but it could be more efficient if it would not involve dropping static data with each run. However, since addressing this issue would make the RFC more complex, we have decided to implement this optimization at a later stage, and focus on creating a first proof of concept for now. + +## Alternatives +An alternative approach could involve using a generic block type called `JsonInterpreter` instead of the proposed `GtfsRTInterpreter`. This block type would parse incoming files into a new io-datatype called `JSON`. A downstream block type `JsonFlattener` would then flatten the `JSON` into a tabular sheet representation (eg. by having a file map to some form of tree structure for JSON which maps to sheets). Since the flattening of generic JSON files into tabular representation is a fundamental topic for ETL systems, this approach should be discussed in a separate RFC as it falls outside the scope of the master thesis. + +## Outlook +Once we want to logically validate the data model during load, we have the dependency to first load the static data and afterwards the realtime data because realtime depends on static. We just want to load realtime data to the sink, if the data is conform to the static data which was loaded in advance. Unfortunally state now we cannot model this sequential dependency in one pipeline(eg. by connecting the GTFS-sink with the GTFS-RT-Extractor) but to shed light on this the dependcy this is mentioned as an outlook. diff --git a/rfc/0006-gtfs-rt-support/gtfs-and-gtfs-rt-pipeline.png b/rfc/0006-gtfs-rt-support/gtfs-and-gtfs-rt-pipeline.png new file mode 100644 index 00000000..b810e187 Binary files /dev/null and b/rfc/0006-gtfs-rt-support/gtfs-and-gtfs-rt-pipeline.png differ diff --git a/rfc/0006-gtfs-rt-support/gtfs-and-gtfs-rt-pipeline.png.license b/rfc/0006-gtfs-rt-support/gtfs-and-gtfs-rt-pipeline.png.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/rfc/0006-gtfs-rt-support/gtfs-and-gtfs-rt-pipeline.png.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/rfc/0006-gtfs-rt-support/simple-gtfs-rt-pipeline.png b/rfc/0006-gtfs-rt-support/simple-gtfs-rt-pipeline.png new file mode 100644 index 00000000..104bb0e9 Binary files /dev/null and b/rfc/0006-gtfs-rt-support/simple-gtfs-rt-pipeline.png differ diff --git a/rfc/0006-gtfs-rt-support/simple-gtfs-rt-pipeline.png.license b/rfc/0006-gtfs-rt-support/simple-gtfs-rt-pipeline.png.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/rfc/0006-gtfs-rt-support/simple-gtfs-rt-pipeline.png.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/rfc/0006-gtfs-rt-support/visualization-gtfs-file-collection-including-gtfs-rt.png b/rfc/0006-gtfs-rt-support/visualization-gtfs-file-collection-including-gtfs-rt.png new file mode 100644 index 00000000..f1cc132e Binary files /dev/null and b/rfc/0006-gtfs-rt-support/visualization-gtfs-file-collection-including-gtfs-rt.png differ diff --git a/rfc/0006-gtfs-rt-support/visualization-gtfs-file-collection-including-gtfs-rt.png.license b/rfc/0006-gtfs-rt-support/visualization-gtfs-file-collection-including-gtfs-rt.png.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/rfc/0006-gtfs-rt-support/visualization-gtfs-file-collection-including-gtfs-rt.png.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/rfc/0007-atomic-valuetypes/README.md b/rfc/0007-atomic-valuetypes/README.md new file mode 100644 index 00000000..6f5050da --- /dev/null +++ b/rfc/0007-atomic-valuetypes/README.md @@ -0,0 +1,228 @@ +<!-- +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only +--> + +# RFC 0007: Atomic Valuetypes + +| | | +|-------------|---------------------| +| Feature Tag | `atomic-valuetypes` | +| Status | `ACCEPTED` | +| Responsible | `@felix-oq` | +<!-- + Status Overview: + - DRAFT: The RFC is not ready for a review and currently under change. Feel free to already ask for feedback on the structure and contents at this stage. + - DISCUSSION: The RFC is open for discussion. Usually, we open a PR to trigger discussions. + - ACCEPTED: The RFC was accepted. Create issues to prepare implementation of the RFC. + - REJECTED: The RFC was rejected. If another revision emerges, switch to status DRAFT. +--> + +## Summary + +This RFC introduces an initial, limited valuetype concept that allows users to define custom atomic valuetypes +based on builtin primitive valuetypes. Common constraints can be defined and applied to valuetypes, allowing to restrict the +set of valid values for a given valuetype. + +## Motivation + +As open data is not bound to a particular domain, users need a way to define domain-specific semantics in regard +to values. This RFC proposes a domain-agnostic way to accomplish this using user-defined valuetypes. + +A valuetype is used to define a set of valid values, so invalid values can easily be identified. In many cases, it +is sufficient to specify a valuetype based on a primitive valuetype (like `text` or `decimal`) and apply +constraints to further limit the set of values. Consider the following excerpt from a [dataset found on mobilithek.info]( +https://mobilithek.info/offers/-8739430008147831066): + +| EVA_NR | DS100 | IFOPT | NAME | Verkehr | Laenge | Breite | Betreiber_Name | Betreiber_Nr | Status | +|:-------:|:-----:|:---------------:|:------------------:|:-------:|:---------:|:---------:|:-------------------------:|:------------:|:------:| +| 8002551 | AELB | de:02000:11943 | Hamburg Elbbrücken | RV | 10,0245 | 53,5345 | DB Station und Service AG | | neu | +| 8001510 | TDSA | de:08237:8009:2 | Dornstetten-Aach | RV | 8,48291 | 48,4733 | DB Station und Service AG | | neu | +| 8001966 | MFOL | de:09187:90183 | Feldolling | nur DPN | 11,852244 | 47,895336 | DB Station und Service AG | | neu | + +Some values in the columns could be described using valuetypes, e.g. `Laenge` and `Breite` are decimals ranging +from -90 to 90 as they are supposed to be geographic coordinates. Further examples for valuetypes will be addressed +in upcoming sections. + +## Explanation + +### User-defined valuetypes based on primitive valuetypes + +Valuetypes are defined using the `valuetype` keyword, giving a name and specifying the underlying primitive valuetype. +constraints can be added via the `constraints` collection, see [this upcoming section](#adding-constraints-to-valuetypes) +for details. + +Here are some examples: + +```jayvee +valuetype IFOPT oftype text { + constraints: [ ... ]; +} + +valuetype Longitude oftype decimal { + constraints: [ ... ]; +} +``` + +### Defining constraints for valuetypes + +Constraints can be defined using the `constraint` keyword, providing a name and by selecting the type of +constraint. There are several builtin constraints to choose from, see +[the next section](#builtin-types-of-constraints) for details. Constraints are configured by assigning values to attributes (similar +to block types). + +Some examples: + +```jayvee +constraint IFOPT_Format oftype RegexConstraint { + regex: /[a-z]{2}:\d+:\d+(:\d+)?/; +} +``` + +```jayvee +constraint TrafficValueRange oftype WhitelistConstraint { + whitelist: [ "FV", "RV", "nur DPN" ]; +} +``` + +```jayvee +constraint GeographicCoordinateRange oftype RangeConstraint { + lowerBound: -90; + lowerBoundInclusive: true; + upperBound: 90; + upperBoundInclusive: true; +} +``` + +#### Builtin types of constraints + +There are a number of constraints that are built into the language. They should be sufficient to cover most use cases. + +##### For any primitive valuetype + +**`WhitelistConstraint`**: Configured via a collection of values to be considered valid. Any other values are +considered invalid. + +**`BlacklistConstraint`**: Configured via a collection of values that are considered invalid. + +##### Specifically for `text` + +**`RegexConstraint`**: Configured via a regex. Only text values that are matching the given regex are considered valid. + +**`LengthConstraint`**: Configured via a minimum and/or a maximum length for text values to be considered valid. + +##### Specifically for `decimal` and `integer` + +**`RangeConstraint`**: Configured via a lower and an upper bound (each either inclusive or exclusive). Only values in +that range are considered valid. + +### Adding constraints to valuetypes + +To add constraints to a valuetype, they have to be added to the `constraints` collection of that valuetype: + +```jayvee +valuetype MyValuetype oftype text { + constraints: [ + MyRegexConstraint, + MyLengthConstraint + ]; +} + +constraint MyRegexConstraint oftype RegexConstraint { + // ... +} + +constraint MyLengthConstraint oftype LengthConstraint { + // ... +} +``` + +Note that the constraints have to be suitable regarding the primitive valuetype. + +### Syntactic sugar for common constraints + +There are syntactic sugar variants for common constraints. They can be used within the `constraints` collection of +a valuetype. Using such a syntax implicitly creates a corresponding constraint with no name. + +#### For `RegexConstraint`: + +```jayvee +valuetype IFOPT oftype text { + constraints: [ + /[a-z]{2}:\d+:\d+(:\d+)?/ + ]; +} +``` + +#### For `WhitelistConstraint`: + +Enumeration of all valid values, each separated with `|`: + +```jayvee +valuetype DB_Traffic oftype text { + constraints: [ + "FV" | "RV" | "nur DPN" + ]; +} +``` + +#### For `RangeConstraint`: + +```jayvee +valuetype Longitude oftype decimal { + constraints: [ + -90 <= value <= 90 + ]; +} +``` + +In this context, `value` is a keyword that stands for the value being inspected. Conceptually, the expression above +desugars to `-90 <= value AND value <= 90`. + +Note that it is also allowed to use `<` instead of `<=` for exclusive bounds. + +### Assigning valuetypes to columns + +The assignment of a user-defined valuetype to a column is similar to how it is done with primitive valuetypes: + +```jayvee +block DbStopsTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "EVA_NR" oftype integer, + "DS100" oftype text, + // Here we assign a user-defined valuetype to the column "IFOPT" + "IFOPT" oftype IFOPT, + "NAME" oftype text, + "Verkehr" oftype DB_Traffic, + "Laenge" oftype Longitude, + "Breite" oftype Latitude, + "Betreiber_Name" oftype text, + "Betreiber_Nr" oftype integer, + "Status" oftype text + ]; +} +``` + +## Drawbacks + +- No syntactic sugar available for reusable constraints +- No arbitrary boolean expressions can be used to define custom constraints +- Users are unable to apply boolean operators to constraints other than `AND` + +## Alternatives + +- Allow arbitrary constraints using boolean expressions that can be combined with boolean operators + - Unsure how this could be expressed, esp. when constraints of a valuetype are written down as a collection + - The syntactic sugar versions in this RFC could be turned into inline boolean expressions + +## Possible Future Changes/Enhancements + +- Introduce enums as a language feature rather than implicitly via a `text` valuetype with a `WhitelistConstraint` + applied +- Readers and writers to handle different value representations +- Mechanisms for handling invalid values +- More mighty constraints using arbitrary boolean expressions +- Concept for composite value types +- Concept for SI units and operators diff --git a/rfc/0008-builtin-blocktypes/README.md b/rfc/0008-builtin-blocktypes/README.md new file mode 100644 index 00000000..40703d77 --- /dev/null +++ b/rfc/0008-builtin-blocktypes/README.md @@ -0,0 +1,212 @@ +<!-- +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only +--> + +# RFC 0008: Builtin blocktypes + +| | | +|-------------|----------------------| +| Feature Tag | `builtin-blocktypes` | +| Status | `ACCEPTED` | <!-- Possible values: DRAFT, DISCUSSION, ACCEPTED, REJECTED --> +| Responsible | `@rhazn` (written by `@felix-oq`) | +<!-- + Status Overview: + - DRAFT: The RFC is not ready for a review and currently under change. Feel free to already ask for feedback on the structure and contents at this stage. + - DISCUSSION: The RFC is open for discussion. Usually, we open a PR to trigger discussions. + - ACCEPTED: The RFC was accepted. Create issues to prepare implementation of the RFC. + - REJECTED: The RFC was rejected. If another revision emerges, switch to status DRAFT. +--> + +## Summary + +Blocktypes that are built into the language are declared syntactically by denoting their inputs, outputs and properties. +This change does not alter any semantics, but it can serve as a foundation for composite block types in an upcoming RFC. + +## Motivation + +Currently, all blocktypes only exist implicitly in the language. +To make their definition more explicit, we should support the declaration of builtin blocktypes (i.e. blocktypes that are built into the language). +This allows users to look up inputs, outputs and properties without having to look at the documentation. + +## Explanation + +- `builtin` keyword to indicate that the blocktype is built into the language + - Produces an error if the blocktype is unknown +- `input` / `output` keywords to type the input and output of the blocktype + - If omitted, a blocktype has no input or output +- `property` keyword to define a property with a name and a type + - Optionally, a default value can be assigned +- Adds new keywords for existing property types: + - Valuetypes: `regex`, `cellrange` / `row` / `column` / `cell`, `valuetypeAssignment` + - Typed collections: `collection<type>` + - Nested collections are not supported for now + - IO types: `file`, `fileSystem`, `textFile`, `sheet`, `table` + +See the following section for concrete code examples. + +### Syntax for current block types + +<details> +<summary>Click to show</summary> + +```jayvee +builtin blocktype HttpExtractor { + property url oftype text; + + output oftype file; +} + +builtin blocktype ArchiveInterpreter { + input oftype file; + + property archiveType oftype text; + + output oftype fileSystem; +} + +builtin blocktype FilePicker { + input oftype fileSystem; + + property path oftype text; + + output oftype file; +} + +builtin blocktype TextFileInterpreter { + input oftype file; + + property encoding oftype text; + property lineBreak oftype regex; + + output oftype textFile; +} + +builtin blocktype TextLineDeleter { + input oftype textFile; + + property lines oftype collection<integer>; + + output oftype textFile; +} + +builtin blocktype TextRangeSelector { + input oftype textFile; + + property lineFrom oftype integer; + property lineTo oftype integer; + + output oftype textFile; +} + +builtin blocktype CSVInterpreter { + input oftype textFile; + + property delimiter oftype text: ","; + property enclosing oftype text: ""; + property enclosingEscape oftype text: ""; + + output oftype sheet; +} + +builtin blocktype CellRangeSelector { + input oftype sheet; + + property select oftype cellrange; + + output oftype sheet; +} + +builtin blocktype CellWriter { + input oftype sheet; + + property write oftype text; + property at oftype cell; + + output oftype sheet; +} + +builtin blocktype ColumnDeleter { + input oftype sheet; + + property delete oftype collection<column>; + + output oftype sheet; +} + +builtin blocktype RowDeleter { + input oftype sheet; + + property delete oftype collection<row>; + + output oftype sheet; +} + +builtin blocktype TableInterpreter { + input oftype sheet; + + property header oftype boolean; + property columns oftype collection<valuetypeAssignment>; + + output oftype table; +} + +builtin blocktype SQLiteLoader { + input oftype table; + + property table oftype text; + property file oftype text; +} + +builtin blocktype PostgresLoader { + input oftype table; + + property host oftype text; + property port oftype integer; + property username oftype text; + property password oftype text; + property database oftype text; + property table oftype text; +} +``` +</details> + +## Drawbacks + +- Adds valuetype keywords for properties, but these types cannot be assigned to table columns (e.g. `regex`) + +## Alternatives + +- Not having declarations for builtin block types at all +- No mixing of property valuetypes and column valuetypes +- Using a less verbose syntax (e.g. omit `property` keyword or use a shorthand operator instead of `oftype` keyword) + +## Possible Future Changes/Enhancements + +- Usage of custom valuetypes for typing properties + - Constraints could then be used to validate property values + - Properties could be considered inputs of a block, so property values can be provided dynamically by pipes +- Include semantics for validating property values beyond valuetypes +- Introduce a standard for documenting blocktypes in the code +- Possibility to declare multiple named inputs and outputs for blocktypes +- Can serve as a foundation for composite blocktypes, e.g.: + +```jayvee +composite blocktype HttpFileExtractor { + property url oftype text; + + output oftype textFile; + + block Extractor oftype HttpExtractor { + url: url; + } + + Extractor -> Interpreter; + + block Interpreter oftype TextFileInterpreter { + } + + Interpreter -> output; +} +``` diff --git a/rfc/0009-expressions/README.md b/rfc/0009-expressions/README.md new file mode 100644 index 00000000..cb3cab72 --- /dev/null +++ b/rfc/0009-expressions/README.md @@ -0,0 +1,107 @@ +<!-- +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only +--> + +# RFC 0009: Simple Expressions + +| | | +|---|---| +| Feature Tag | `Simple Expressions` | +| Status | `ACCEPTED` | <!-- Possible values: DRAFT, DISCUSSION, ACCEPTED, REJECTED --> +| Responsible | `georg-schwarz` | <!-- TODO: assign yourself as main driver of this RFC --> +<!-- + Status Overview: + - DRAFT: The RFC is not ready for a review and currently under change. Feel free to already ask for feedback on the structure and contents at this stage. + - DISCUSSION: The RFC is open for discussion. Usually, we open a PR to trigger discussions. + - ACCEPTED: The RFC was accepted. Create issues to prepare implementation of the RFC. + - REJECTED: The RFC was rejected. If another revision emerges, switch to status DRAFT. +--> + +## Summary + +This RFC introduces a simple expression language for numeric, boolean, and text values. + +## Motivation + +Right now, users have to to define property values of fractions in the decimal representation, e.g., `0.3`. They cannot express non--terminating decimal values or repeating decimal ones, e.g., `1/3` (a third). + +For future features (like value transformations), we also need boolean expressions that allow for numeric comparisons, e.g. `3 > 5` should resolve to `false`. + +## Explanation + +### Operators + +The following operators are in descending order by their precedence: +- `()` parentheses for grouping a sub-expression +- unary operators (see below) +- binary operators (see below) + +When operators of similar precedence don't determine the order, we evaluate from left to right. + +#### Unary Prefix Operator +Binary prefix operators follow the pattern `<operator> <operand>`. Parentheses can be used for grouping larger sub-expressions. +The following operators share the same precedence: +- `floor`, `ceil`, `round` for conversion of decimal numbers to integers. +- `sqrt` for calculating the square root on numbers. +- `not` for unary inversion of boolean values + +#### Binary Operators +Binary infix operators follow the infix pattern `<left-operand> <operator> <right-operand>`. +The following operators are in descending order by their precedence: +- `pow`, `root` for power and root calculation. E.g., `2 pow 3` is 2³, `5 root 2` is `sqrt(5). +- `*`, `/`, `%` for multiplication, division, and remainder +- `+`, `-` for addition and subtraction +- `<`, `<=`, `>`, `>=` for relational operators comparing numbers +- `==`, `!=` for equality operators comparing numbers, booleans, and texts +- `and` for a logical AND on booleans +- `xor` for a logical XOR on booleans +- `or` for a logical OR on booleans + + +### Handling of booleans + +The expression resolving of boolean does follow the common pattern. + + +### Handling of numbers + +Division operator and multiplication/addition/subtraction with at least one decimal operand **always** produce `decimal` values. To convert to an integer value, the operators `floor`, `ceil`, `round` have to be used. + +Division by zero throws an error. + +Comparison of different number types (e.g., integers and decimals) is solved by converting first to the less restrictive value type (in the example, to decimals). + +### Handling of texts + +We currently only support equality check on text values + + +### Interplay of different primitive value types + +We infer the primitive value type based on the operator. The resulting value type of each operator is unambiguous. + +The operands `==` and `!=` are the only ones that allow operands of different value types. Other operators are unambiguous. Comparing the equality of two operands of different value types throws an error, unless they are different types of numbers that can be converted to the less restrictive type (e.g., integers are converted to decimals when compared). + + +## Drawbacks + +- Some operators are functions in other languages which might be unintuitive. +- Always converting to decimals on division might lead to misleading results if the programmer is not aware. + +## Alternatives + +- Use an established scripting language instead (e.g. Lua script). +- Automatically convert to integer values based on some criteria. +- Make `pow` and `root` prefix operators, e.g., `root(5, 2)`. +- Require grouping brackets on unary operators. + +## Possible Future Changes/Enhancements + +- Incorporate operators for `text` values (append, split, get length, ...). +- Access input data (e.g., sheet cells) to implement value transformations (#213). +- Division by zero produces an INVALID value. +- Division and multiplication/addition/subtraction with at least one decimal operand might statically evaluate to an integer if no variables are used and the result has no decimals. +- Boolean expressions could be used to describe custom constraints. +- Depending on future design decisions, value transformations may relate more to value representation concepts (e.g., value readers and writers) rather than blocks. diff --git a/rfc/0010-constraint-expressions/README.md b/rfc/0010-constraint-expressions/README.md new file mode 100644 index 00000000..f51d7495 --- /dev/null +++ b/rfc/0010-constraint-expressions/README.md @@ -0,0 +1,132 @@ +<!-- +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only +--> + +# RFC 0010: Constraints as Expressions + +| | | +|---|---| +| Feature Tag | `constraints-as-expressions` | +| Status | `ACCEPTED` | <!-- Possible values: DRAFT, DISCUSSION, ACCEPTED, REJECTED --> +| Responsible | `georg-schwarz` | +<!-- + Status Overview: + - DRAFT: The RFC is not ready for a review and currently under change. Feel free to already ask for feedback on the structure and contents at this stage. + - DISCUSSION: The RFC is open for discussion. Usually, we open a PR to trigger discussions. + - ACCEPTED: The RFC was accepted. Create issues to prepare implementation of the RFC. + - REJECTED: The RFC was rejected. If another revision emerges, switch to status DRAFT. +--> + +## Summary + +This RFC presents an alternative representation of value type constraints using expressions. It replaces the current ConstraintTypes (but not the existing ConstraintType mechanism itself that will us allow to add more complex domain-specific constraints that are not expressible by expressions). + +## Motivation + +Defining constraints is currently very bloated on the syntax level. + +Example: +``` +constraint HundredScale oftype RangeConstraint { + lowerBound: 1; + lowerBoundInclusive: false; + upperBound: 100; +} +``` + +## Explanation + +Instead of using this block-like syntax, we can use expressions to define constraints. + +The general syntax looks like this: +``` +constraint <name> on <primitive value type>: <expression with parameter value, evaluating to boolean> +``` + +### Examples + +The following subsections will go over the existing constraints and showcase them in an example. + +#### AllowlistConstraint + +``` +constraint TimeUnitString on text: + value =="ms" or + value == "s" or + value == "min" or + value == "h" or + value == "d" or + value == "m" or + value == "y"; +``` + +#### DenylistConstraint +``` +constraint NoPrimaryColors on text: + not (value == "red" or value == "blue" or value == "yellow"); + +// alternative +constraint NoPrimaryColors on text: + value != "red" and + value != "blue" and + value != "yellow"; +``` + +#### LengthConstraint +``` +constraint JavaStringLength on text: + value.length >= 0 and value.length <= maxLength; +``` + +#### RangeConstraint +``` +constraint HundredScale on decimal: + value > 1 and value <= 100; +``` + +#### RegexConstraint +``` +constraint IFOPT_Format on text: + value matches /[a-z]{2}:\d+:\d+(:\d+)?(:\d+)?/; +``` + +### Hidden Enhancements +This RFC introduces / relies of some features that are not implemented yet: +* Expressions allow parameters (keyword `value`). +* The length of text parameters is accessible by `value.length`. Evaluates to an integer (zero or positive). +* The binary operator `matches` is introduced which evaluates to a boolean value. The usage is `<text> matches <regex>`. + +### Usage in ValueTypes +In ValueTypes, these constraints can be added like before by name. Combining constraints on non-matching value types leads to a diagnostic error at compile-time (by the language server). +``` +valuetype GasFillLevel oftype decimal { + constraints: [ GasFillLevelRange ]; +} +``` + +Alternatively, constraints can be defined inline with the caveat that they are not reusable elsewhere. Then the value, that the `value` keyword refers to, adheres to the type of the enclosing ValueType. +``` +valuetype GasFillLevel oftype decimal { + constraints: [ value >= 0 and value <= 100 ]; +} +``` + + +## Drawbacks +- Requires the features in the "Hidden Enhancements" section in order to match the current functionality. + +## Alternatives + +- Syntax to define parameter in constraint could be different, e.g., `constraint MyConstraint: (number) => <expression>`. +- Put the expression into a `{}`-wrapped scope for consistency (probably together with if/else statements, looks more like a method). + + + +## Possible Future Changes/Enhancements +- Allow if/else statements in a more imperative way rather than packing everything into one expression. +- Allow renaming of `value` to a custom parameter name, e.g., `for text as x`. +- add `in` operator for collections for syntactic sugar. + + diff --git a/rfc/0011-value-transformer/README.md b/rfc/0011-value-transformer/README.md new file mode 100644 index 00000000..f52b75d0 --- /dev/null +++ b/rfc/0011-value-transformer/README.md @@ -0,0 +1,129 @@ +<!-- +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only +--> + +# RFC 0011: Value Transform + +| | | +|---|---| +| Feature Tag | `value-transform` | +| Status | `ACCEPTED` | <!-- Possible values: DRAFT, DISCUSSION, ACCEPTED, REJECTED --> +| Responsible | `georg-schwarz` | +<!-- + Status Overview: + - DRAFT: The RFC is not ready for a review and currently under change. Feel free to already ask for feedback on the structure and contents at this stage. + - DISCUSSION: The RFC is open for discussion. Usually, we open a PR to trigger discussions. + - ACCEPTED: The RFC was accepted. Create issues to prepare implementation of the RFC. + - REJECTED: The RFC was rejected. If another revision emerges, switch to status DRAFT. +--> + +## Summary + +This RFC introduces the concept of Value Transform functions that allow reading values, writing values, and transforming values. + +**Note:** This RFC focuses enhancing the current state but has an elaborate "Possible Enhancements" section that showcases how this concept can be extended in the future. + +## Motivation + +Right now, we can only "parse" values but not manipulate them in order to clean data. + +## Explanation + +``` +transform <name> { + from <inputName> oftype <valuetype>; + to <outputName> oftype <valuetype>; + + <outputName>: <expression producing output valuetype>; +} +``` + +A transform can map one input to one output with their types. +See future enhancements for multiple inputs and outputs. + +The output value is assigned (`:`) to an expression to produces the output values. + +### Example: Simple numeric transformation +``` +transform CelsiusToKelvin { + from tempCelsius oftype decimal; + to tempKelvin oftype decimal; + + tempKelvin: tempCelsius + 273.15; // simple calculation +} +``` + +### Usage: TableTransformerBlock +There is a newly introduced `TableTransformerBlock` block type that applies the mapper to a column in a `table`. + +Example: +``` +block GermanToBooleanBlock oftype TableTransformerBlock { + inputColumn: "Kritisch"; // to satisfy the input of the transform + outputColumn: "IsCritical"; // might also overwrite the input column if name is identical + use: GermanToBooleanTransformer; +} +``` + + +## Drawbacks +- declaration of in and outputs might be much boilerplate. + +## Alternatives + +- use other keyword, e.g., `vtrans`, `mapper`, ... +- use `input`/`output` keywords instead of `from`/`to`, or even shorter `in`/`out` +- Transformers can work on sheets +- refactor TableInterpreter to also allow transforms + +## Possible Future Changes/Enhancements + +- We could introduce a pipe-like syntax that allows chaining transforms. +- Support multiple inputs and outputs + +### Variables in Transformers +Storing interim results can improve coding experience. +``` +transform <name> { + // inputs and outputs + var <varBane> oftype <valuetype>: <expression producing valuetype>; // alternative: infer type automatically + // use variable to compute outputs +} +``` + +### Composite ValueTypes + +Composite ValueTypes could be served with this implementation by extending the assignment statement: +``` +// "MyStreet" and "7b" => composite valuetype +transform AddressComposer { + from streetName oftype text; + from houseNumber oftype text; + to address oftype Address; // has properties "streetName" oftype StreetName and "houseNumber" oftype HouseNumber + + // "." notation to write on property of composite value type + address.streetName: streetName using StreetNameReader; // StreetNameReader is other transformation text -> StreetName + address.houseNumber: houseNumber using HouseNumberReader; // HouseNumberReader is other transformation text -> HouseNumber +} +``` + +### MatchOn + +A mapping expression might enable easier string matching. They are syntactic sugar for if-elseif cascades. + +``` +// reads "23.4" and "$" into a compound value type +transform CelsiusToKelvin { + from amountRaw oftype text; + from currencyRaw oftype text; + to balance oftype Balance; // compound type with fields "amount" and "currency" + + balance.amount: amountRaw as decimal; // simple parsing does not need a mapping + balance.currency: matchOn currencyRaw { // matching + /^(EUR|€)$/ => "EUR"; + /^(USD|$)$/ => "USD"; + }; +} +``` \ No newline at end of file diff --git a/rfc/0013-lossless-arithmetic/0013-lossless-arithmetic.md b/rfc/0013-lossless-arithmetic/0013-lossless-arithmetic.md new file mode 100644 index 00000000..280afe75 --- /dev/null +++ b/rfc/0013-lossless-arithmetic/0013-lossless-arithmetic.md @@ -0,0 +1,63 @@ +<!-- +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only +--> + +# RFC 0013: Lossless arithmetic + +| | | +|---|---| +| Feature Tag | `Lossless arithmetic` | <!-- TODO: choose a unique and declarative feature name --> | +| Status | `DISCUSSION` | <!-- Possible values: DRAFT, DISCUSSION, ACCEPTED, REJECTED --> | +| Responsible | `@rhazn` | <!-- TODO: assign yourself as main driver of this RFC --> | + +<!-- + Status Overview: + - DRAFT: The RFC is not ready for a review and currently under change. Feel free to already ask for feedback on the structure and contents at this stage. + - DISCUSSION: The RFC is open for discussion. Usually, we open a PR to trigger discussions. + - ACCEPTED: The RFC was accepted. Create issues to prepare implementation of the RFC. + - REJECTED: The RFC was rejected. If another revision emerges, switch to status DRAFT. +--> + +## Summary + +As we built out Jayvee and define (arithmetric) expressions, we are not defining a formal semantic of how they are evaluated but rely on our interpreter implementation. With this RFC, we define our goal to support mathematically correct arithmetric expressions that are not limited by JavaScript idiosyncrasies when dealing with numbers. + +## Motivation + +Jayvee is a language to model data pipelines and will therefore handle numeric data as well. For this data, it is important that it arrives in a sink as correctly as possible. Number representation and evaluation of arithmetric expressions has various edge cases in any programming languages, especially in Javascript. Our goal should be to be as correct as possible. + +## Explanation + +The Jayvee interpreter should correctly compare numbers. The Jayvee expression `3.00000000000000000000000000000001 == 3` must evaluate to `false`, not `true`. + +Due to the use of javascript for the interpreter, it will evaluate to `true` without special handling. + +```javascript +3.00000000000000000000000000000001 === 3 +true +``` + +### Use appropriate data types for numbers + +- Jayvee `integer` should behave as [Integer](https://en.wikipedia.org/wiki/Integer) +- Jayvee `decimal` should behave as [Rational number](https://en.wikipedia.org/wiki/Rational_number) + +## Drawbacks + +- Performance will suffer from non-native number types + +## Alternatives + +- Accept limitations of the interpreter implementation + +## Possible Future Changes/Enhancements +- Jayvee should implement a value type for [Real numbers](https://en.wikipedia.org/wiki/Real_number) to correctly handle irrational numbers like `sqrt(2)` which can already be created in expressions +- User experiments should be done for an intuitive naming of built-in numberic value types, consider renaming `decimal` to `rational`. + +## Implementation notes +- Find an appropriate library for decimal handling + - Consider [decimal.js](https://github.com/MikeMcl/decimal.js/) and [mathjs](https://github.com/josdejong/mathjs) +- Research if [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt) should be used for `integer`s instead of numbers +- Delay expression evaluation as to as late as possible diff --git a/rfc/0014-attribute-based-valuetypes/README.md b/rfc/0014-attribute-based-valuetypes/README.md new file mode 100644 index 00000000..96c53028 --- /dev/null +++ b/rfc/0014-attribute-based-valuetypes/README.md @@ -0,0 +1,88 @@ +<!-- +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only +--> + +# RFC 0014: Attributed-based syntax for defining value types + +| | | +|---|---| +| Feature Tag | `attribute-based value types` | <!-- TODO: choose a unique and declarative feature name --> +| Status | `DRAFT` | <!-- Possible values: DRAFT, DISCUSSION, ACCEPTED, REJECTED --> +| Responsible | `dirkriehle` | <!-- TODO: assign yourself as main driver of this RFC --> +<!-- + Status Overview: + - DRAFT: The RFC is not ready for a review and currently under change. Feel free to already ask for feedback on the structure and contents at this stage. + - DISCUSSION: The RFC is open for discussion. Usually, we open a PR to trigger discussions. + - ACCEPTED: The RFC was accepted. Create issues to prepare implementation of the RFC. + - REJECTED: The RFC was rejected. If another revision emerges, switch to status DRAFT. +--> + +## Summary + +I'd like to allow the definition of user-defined single-attribute value types. In addition to the current syntax, in which an underlying value type is referenced through 'oftype', it does not use instantiation/inheritance but rather composition. + +## Motivation + +The purpose of using composition is that it is + +1. more similar to traditional approaches and +2. is needed anyway for multi-attribute value types. + +## Explanation + +To define a value type like CorrelationCoefficient and its range of -1 to +1, we have to write: + +```jayvee +valuetype CorrelationCoefficient oftype decimal { + constraints: [ + MinusOneToPlusOneRange, + ]; +} +``` + +This syntax is smart in that you don't have to list and name an attribute but rather rely on an implicit 'value' attribute. Still, I propose to make that attribute explicit. New syntax would be: + +```jayvee +valuetype CorrelationCoefficient { + attributes: [ + value oftype decimal; + ]; + constraints: [ + MinusOneToPlusOneRange; // Don't know how to attach this to value attribute + ]; +} +``` + +While more verbose, it prepares the way for + +```jayvee +valuetype Money { + attributes: [ + amount oftype decimal; + currency oftype Currency; + ]; +} +``` +which we'll need anyway. Conceivably, the step to multi-attribute value types could be merged with this one, but I simply wanted to try the RFC process rather than keep sending email ;-) + +<!-- + TODO: Explain the details of the RFC. + If the RFC contains more than a single cohesive aspect, structure this section accordingly. + Make sure to provide realistic modelling examples on the example data set introduced above. +--> + +## Drawbacks + +1. Introduces a redundant syntax, +2. Creates extra work/may be too difficult, and +3. May be disruptive to how the language currently works. + +## Alternatives + +Conceivably, we could use multiple inheritance for multi-attribute value types... just joking. + +## Possible Future Changes/Enhancements + +This proposal is to prepare the way for multi-attribute value types. diff --git a/rfc/0015-multi-file-jayvee/0015-multi-file-jayvee.md b/rfc/0015-multi-file-jayvee/0015-multi-file-jayvee.md new file mode 100644 index 00000000..949d1d0a --- /dev/null +++ b/rfc/0015-multi-file-jayvee/0015-multi-file-jayvee.md @@ -0,0 +1,200 @@ +<!-- +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only +--> + +# RFC 0015: Multi-file Jayvee + +| | | +| ----------- | --------------- | --------------------------------------------------------------- | +| Feature Tag | `multi-file` | +| Status | `ACCEPTED` | <!-- Possible values: DRAFT, DISCUSSION, ACCEPTED, REJECTED --> | +| Responsible | `georg-schwarz` | <!-- TODO: assign yourself as main driver of this RFC --> | + +<!-- + Status Overview: + - DRAFT: The RFC is not ready for a review and currently under change. Feel free to already ask for feedback on the structure and contents at this stage. + - DISCUSSION: The RFC is open for discussion. Usually, we open a PR to trigger discussions. + - ACCEPTED: The RFC was accepted. Create issues to prepare implementation of the RFC. + - REJECTED: The RFC was rejected. If another revision emerges, switch to status DRAFT. +--> + +## Summary + +This RFC introduces the possibility of distributing a Jayvee program over multiple files. +This feature will foster reuse of valuetypes, blocks, and other elements. +Inherent to this feature is a concept of how scoping and naming is handled for nested structures. +This RFC introduces two concepts: + +- Element usage from other files, and +- Packages + +## Motivation + +Right now, Jayvee users can only pack their whole Jayvee model into one file. +The challenge is two-fold: + +1. Larger projects will become unmaintainable quite quickly, as the elements cannot be organized into multiple files. +2. Without distribution to multiple files, there is no possibility to later reuse models of other projects. + +## Explanation + +### Element visibility + +We distinguish different kinds of visibilities of elements: + +- `file-private`: usable only within the same file +- `file-published`: usable also in other files of same project +- `package-private`: usable only within the package +- `package-published`: usable also in other locations (since `package` is always `file-published`) + +By introducing the concept of `packages`, most elements can be defined on three scope levels in a Jayvee file: + +1. file-scope (not contained in a further structure, like a pipeline or a package) +2. pipeline-scope (contained in a pipeline) +3. (new) package-scope (contained in a package) + +The name of an element is given by its definition. +The **qualified name** is constructed by prepending container structures in this pattern: `<container name>.<element name>`, e.g., `MyDomainPackage.MyDomainSpecificValuetype`. + +**Access paths:** + +- _file-scope_ elements can access + - ✅ _file-scope_ elements by their name + - ✅ _package-scope_ elements by their qualified name (`packageName.elementName`) + - ❌ no _pipeline-scope_ elements +- _package-scope_ elements can access + - ✅ _file-scope_ elements by their name + - ✅ elements of the same _package-scope_ by their name + - ✅ elements of another _package-scope_ by their qualified name + - ❌ no _pipeline-scope_ elements +- _pipeline-scope_ elements can access + - ✅ _file-scope_ elements by their name + - ✅ elements within the same _pipeline-scope_ by their name + - ❌ no elements of another _pipeline-scope_ + - ✅ _package-scope_ elements by their qualified name + +**Used elements** of different files are handled **as if they were defined at file scope level**. + +### Publishing elements for usage elsewhere (within the project) + +For publishing single elements, the RFC introduces the keyword `publish` to indicate the visibility `file-published`. +All elements within a file are not published per default, visibility `file-private`. +Explicitly declaring an element as published allows for usage elsewhere. + +**Example publish** + +``` +// define and publish in one syntax +publish valuetype MyValueType1 { + // ... details +} + +// define first +valuetype MyValueType2 { + // ... details +} + +// publish later +publish MyValueType2; + +// publish later under a different name +publish MyValueType2 as MyValueType3; +``` + +### Packages: bundling elements to a package for decoupled usage + +For bundling and publishing elements, the RFC introduces a new concept called `packages`. +A `package` can include `Valuetype`s, `Block`s, `BlockType`s, `Constraint`s, `Transform`s, and further `Package`s. +A package must be of visibility `file-published` and, thus, requires the keyword `publish`. +Elements within a package can be of visibility `package-published` by using the `publish` keyword, or are `package-private` per default. +`package`s within `package`s must be `publish`ed as a consequence. + +**Example package** + +``` +publish package MyDomainPackage { + // definition of a new valuetype as part of the package + publish valuetype MyDomainSpecificValuetype1 { + // ... details of valuetype + } + + // reference to an existing valuetype to make it part of the package + publish MyDomainSpecificValuetype2; + + // ... possibly more elements +} +``` + +The advantage of bundling elements into a `package` is the decoupling from the internal file system structure and logically grouping related elements into one namespace. +Rather than accessing files directly (and needing knowledge what is element is located in which files) users can simply use a whole package with all its elements (and don't need to know in which file the element is originally defined). + +### Using elements + +Only `file-published` (with keyword `publish`) elements can be `use`d in other files. + +#### Usage paths + +When using elements of a file or a package, we have to define where the elements are located. +Jayvee provides the following possibilities: + +- a relative file path, e.g., `use * from './path/to/file.jv';` + +The `use` of elements via a file path decouples from the file system structure by using the element name or a defined alias instead of the file path within the file. + +#### Using published elements of a file (within the same project) + +``` +use * from './path/to/location.jv'; // use all published elements from the file, access via qualified name as if it would be defined at the _file-scope_ +use * as MyWrappingNamespace from './path/to/location.jv'; // use all published elements from the file, access via qualified name as if it was defined in an artificial _package-scope_ (adding a prefix to the qualified name) +use { MyDomainSpecificValuetype1 } from './path/to/location.jv'; // only use the published elements from the file, access via qualified name as if it would be defined at the _file-scope_ +use { MyDomainSpecificValuetype1 as Vt1} from './path/to/location.jv'; // only use the published elements from the file, access via qualified name using the alias +``` + +References to these used elements is by their qualified name (unless altered by an alias). + +#### Using a package + +The `use` syntax is similar to importing an element of a file. +Packages can only be used as a whole. + +``` +use { MyDomainPackage } from './path/to/location.jv'; // only use the named package, access via qualified name +use { + MyDomainPackage1, + MyDomainPackage2 +} from './path/to/location.jv'; // only use the named packages, access via qualified name +use { MyDomainPackage as MyPackageAlias} from './path/to/location.jv'; // only use the named package, access via qualified name using the alias +``` + +References to these used elements is by their qualified name (unless altered by an alias). + +## Decision rationale + +- keywords `publish` and `use` over `export` and `import` since they are less technical and better understandable for subject-matter experts +- concept of "namespaces" is separate from publishing them for reuse in other projects (this RFC only introduces the concept of "namespaces" by the keyword `package`) + +## Drawbacks + +- Two different sharing mechanisms (`publish` keyword, `package` concept`) +- Elements of a pipeline cannot be reused, leading to potentially more slim pipelines and a parallel package +- Langium might not support this scoping mechanism out-of-the-box (more complex implementation) + +## Alternatives + +- "use" syntax without braces, etc., `use MyDomainPackage1, MyDomainPackage2 from './path/to/file.jv';` +- switch around `use` and `from`: `from 'location' use { Element };` +- different keyword for publishing files and packages (e.g., `export`) +- different keyword for using files and packages (e.g., `import`) +- different keyword for renaming published / used elements (e.g., `called`) +- Rather call it `module` or `component` instead of `package` + +## Possible Future Changes/Enhancements + +- build out to use packages of other projects via a package-manager mechanism, e.g., by using an URL as location of a "use" statement + - alternative: introduce new concept `library` +- allow "using" single elements of a package instead of "using" the whole package +- allow additional usage paths, like + - absolute file paths, and + - org/repo combination at a central package registry, e.g., `use * from 'jv:my-org/my-repo';` diff --git a/rfc/0016-block-instantiation-syntax/0016-block-instantiation-syntax.md b/rfc/0016-block-instantiation-syntax/0016-block-instantiation-syntax.md new file mode 100644 index 00000000..ff22c661 --- /dev/null +++ b/rfc/0016-block-instantiation-syntax/0016-block-instantiation-syntax.md @@ -0,0 +1,248 @@ +<!-- +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only +--> + +# RFC 0016: Block Instantiation Syntax + +| | | +|---|---| +| Feature Tag | `block-instantiation-syntax` | +| Status | `ACCEPTED` | <!-- Possible values: DRAFT, DISCUSSION, ACCEPTED, REJECTED --> +| Responsible | `@rhazn` | +<!-- + Status Overview: + - DRAFT: The RFC is not ready for a review and currently under change. Feel free to already ask for feedback on the structure and contents at this stage. + - DISCUSSION: The RFC is open for discussion. Usually, we open a PR to trigger discussions. + - ACCEPTED: The RFC was accepted. Create issues to prepare implementation of the RFC. + - REJECTED: The RFC was rejected. If another revision emerges, switch to status DRAFT. +--> + +## Summary +Following our [Design Principles](https://jvalue.github.io/jayvee/docs/dev/design-principles), Jayvee focuses on describing a goal state instead of how to get there. When creating pipelines, programmers use the pipe syntax `->` to connect previously defined `blocks`. Implicitely, the interpreter creates unnamed singleton instances of those blocks and executes them. + +This implicit creation can lead to confusion between blocks and block instances. In addition, it makes it impossible to create more than one instance of a block, which will prevent the creation of more complex DAGs. + +This RFC introduces minimal syntax changes to allow the creation of block instances, introduces more code structure and clarifies wording. + +## Motivation +It is impossible to create multiple instances of the same block, limiting how complex a DAG can be described. + +## Explanation + +### Current state +#### M2 level (focused on blocks only) +Jayvee currently allows the instantiation of `composite blocktype`s, `builtin blocktype`s and `block`s. `block`s are connected to `blocktype`s by an `oftype` relationship. + +```mermaid +classDiagram +direction BT +class BuiltInBlocktype +class CompositeBlocktype +class Block +class Blocktype { <<abstract>> } + +class OftypeRelationship + +CompositeBlocktype --|> Blocktype +BuiltInBlocktype --|> Blocktype + +OftypeRelationship "1"-->"1" Block: instance +OftypeRelationship "*"-->"1" Blocktype: type +``` + +#### An example model visualized +##### The model +``` +pipeline CarsPipeline { + CarsExtractor + -> CarsTableInterpreter + -> CarsLoader; + + block CarsCSVExtractor oftype CSVExtractor { + url: "https://gist.githubusercontent.com/noamross/e5d3e859aa0c794be10b/raw/b999fb4425b54c63cab088c0ce2c0d6ce961a563/cars.csv"; + } + + block CarsTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "name" oftype text, + "mpg" oftype decimal, + ]; + } + + block CarsLoader oftype SQLiteLoader { + table: "Cars"; + file: "./cars.sqlite"; + } +} +``` + +##### As diagram including M2 level +```mermaid +flowchart BT +subgraph M2 + Pipeline + Block + Blocktype + + BuiltInBlocktype + CompositeBlocktype +end +subgraph M1 + CarsPipeline + CSVExtractor + TableInterpreter + SQLiteLoader + CarsCSVExtractor + CarsTableInterpreter + CarsLoader +end +subgraph M0 + anonymousPipeline1["anonymous"] + + anonymousBlock1["anonymous"] + anonymousBlock2["anonymous"] + anonymousBlock3["anonymous"] +end + +BuiltInBlocktype --"subtype of"--> Blocktype +CompositeBlocktype --"subtype of"--> Blocktype + +CarsPipeline -."instance of".-> Pipeline +CSVExtractor -."instance of".-> CompositeBlocktype +TableInterpreter -."instance of".-> BuiltInBlocktype +SQLiteLoader -."instance of".-> BuiltInBlocktype +CarsCSVExtractor -."instance of".-> Block +CarsTableInterpreter -."instance of".-> Block +CarsLoader -."instance of".-> Block + +CarsCSVExtractor --"oftype"--> CSVExtractor +CarsTableInterpreter --"oftype"--> TableInterpreter +CarsLoader --"oftype"--> SQLiteLoader + +anonymousPipeline1 -."instance of".-> CarsPipeline + +anonymousBlock1 -."instance of".-> CarsCSVExtractor +anonymousBlock2 -."instance of".-> CarsTableInterpreter +anonymousBlock3 -."instance of".-> CarsLoader +``` + +##### As diagram excluding M2 level/instance of relationships +```mermaid +flowchart BT +subgraph M1 + direction BT + CarsPipeline["CarsPipeline : Pipeline"] + CSVExtractor["CSVExtractor : CompositeBlocktype"] + TableInterpreter["TableInterpreter : BuiltInBlocktype"] + SQLiteLoader["SQLiteLoader : BuiltInBlocktype"] + CarsCSVExtractor["CarsCSVExtractor : Block"] + CarsTableInterpreter["CarsTableInterpreter : Block"] + CarsLoader["CarsTableInterpreter : Block"] +end +subgraph M0 + anonymousPipeline1[" : CarsPipeline"] + + anonymousBlock1[" : CarsCSVExtractor"] + anonymousBlock2[" : CarsTableInterpreter"] + anonymousBlock3[" : CarsLoader"] +end + +CarsCSVExtractor --"oftype : OftypeRelationship"--> CSVExtractor +CarsTableInterpreter --"oftype : OftypeRelationship"--> TableInterpreter +CarsLoader --"oftype : OftypeRelationship"--> SQLiteLoader +M0 ~~~ M1 +``` + +### Proposed changes +#### 1. Clarify naming +- Across M-levels, we have only a `instance of` relationship +- The only other currently possible relationship is the `oftype` relationship between a `block` and a `blocktype` on M1 level. Other relationships are not possible since they are not modelled at M2 level. +- Instantiation of M2 level concepts in M1 is done with language keywords + - `composite blocktype <name>` / `builtin blocktype <name>` +- We keep `block` as M1 level keyword to instantiate the M2 level `Block` +- Users can describe M0 level entities in a Jayvee model + - For blocks, we call these instances **"block instances"** + - Pipes always connect block instances, never blocks + +#### 2. Enable creating named block instances +- We introduce the following syntax + - `<instance name> : <block>` models a block instance named `<instance name>` of `<block>`. For example: `myCarsCSVExtractor : CarsCSVExtractor` models an instance of the block `CarsCSVExtractor`, named `myCarsCSVExtractor` + - ` : <block>` models an anonymous instance of `<block>`, reusing the same syntax creates a new instance + - `<block>` models a singleton instance of `<block>`, reusing the same syntax reuses the same instance + +#### 3. Allow only pipes and block instances in pipelines +The following explanations use blocks as an example but map directly to **Constraints** and **Valuetypes**. That is, constraints and valuetypes can also only be defined outside of pipelines and are instantiated inside of pipelines. + +- Pipelines may only contain pipes and block instances, everything else has to be described outside of a pipeline + +New syntax (minimal cars example): +``` +// Pipeline containing block instantiations +pipeline CarsPipeline { + myExtractor : CarsCSVExtractor + -> myInterpreter : CarsTableInterpreter + -> myLoader : CarsLoader; +} + +// Block definitions outside of pipeline +block CarsCSVExtractor oftype CSVExtractor { + url: "https://gist.githubusercontent.com/noamross/e5d3e859aa0c794be10b/raw/b999fb4425b54c63cab088c0ce2c0d6ce961a563/cars.csv"; + enclosing: '"'; +} +block CarsTableInterpreter oftype TableInterpreter { + header: true; + columns: [ + "mpg" oftype decimal, + "cyl" oftype integer, + "disp" oftype decimal, + ]; +} +block CarsLoader oftype SQLiteLoader { + table: "Cars"; + file: "./cars.sqlite"; +} +``` + +Composite blocks are a special case that use a smiliar pipe syntax to chain blocks. For the scope of this RFC, chaining pipeline syntax should **stay possible in pipelines and composite blocks** while **composite blocks are also able to contain block definitions**. In a future enhancement (see Possible Future Changes/Enhancements), this should be split up with the introduction of packages (ref [RFC0015](../0015-multi-file-jayvee/0015-multi-file-jayvee.md)). + +#### 4. Allow only one DAG per pipeline +- If a user creates two graphs in one pipeline, the pipeline definition becomes invalid + - A Jayvee model can contain more than one pipeline definition + - All pipelines in a Jayvee model are instantiated and executed when the model is interpreted +``` +// Valid pipeline +pipeline examplePipeline { + A -> B -> C; + B -> D; +} + +// Invalid pipeline +pipeline examplePipeline { + A -> B -> C; + D -> E; +} + +// Alternative to the invalid pipeline, create two pipelines +pipeline examplePipeline1 { + A -> B -> C; +} +pipeline examplePipeline2 { + D -> E; +} +``` + +## Drawbacks +- With additional enforced structure, users loose flexibility to define things where they want + +## Alternatives +- Introduce explicit execution calls like `pipeline.execute();` or `instantiate <pipeline name>;`/`run <pipeline name>;` instead of implicitly executing pipelines + - Decided against because Jayvee is purely declarative right now outside of expressions + +## Possible Future Changes/Enhancements +- After implementing [RFC0015](../0015-multi-file-jayvee/0015-multi-file-jayvee.md), reconsider disallowing block definitions in composite blocks and instead enforce only chained pipelines in composite blocks and distribute them and their block definitions using packages instead. +- We consider constructors out of scope as of now, but save the following thoughts here for future reference + - Constructors should always use named parameters, never parameter order + - Constructor calls should be placed after the type, e.g., `<name> : <block>()` as in `myCarsCSVExtractor : CarsCSVExtractor()` diff --git a/tools/scripts/add-package-json-version.mjs b/tools/scripts/add-package-json-version.mjs new file mode 100644 index 00000000..f23ef029 --- /dev/null +++ b/tools/scripts/add-package-json-version.mjs @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + writePackageJson, parsePackageJson, getOutputPath +} from './shared-util.mjs'; +import { workspaceRoot } from "@nx/devkit"; + +// Executing this script: node path/to/add-package-json-version.mjs {projectName} +const [, , projectName] = process.argv; + +process.chdir(workspaceRoot); +const rootPackageJson = parsePackageJson(); + +process.chdir(getOutputPath(projectName)); + +const packageJson = parsePackageJson(); +packageJson.version = rootPackageJson.version; +writePackageJson(packageJson); diff --git a/tools/scripts/check-for-invalid-windows-paths.mjs b/tools/scripts/check-for-invalid-windows-paths.mjs new file mode 100644 index 00000000..4ff57251 --- /dev/null +++ b/tools/scripts/check-for-invalid-windows-paths.mjs @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import * as fs from 'node:fs'; +import path from 'node:path'; + +/** + * Checks if a path contains characters not valid for windows. + * @param filePath {string} + * @returns {boolean} + */ +function containsInvalidCharactersForWindows(filePath) { + const invalidCharsRegex = /[<>:"/\\|?*]/; + return invalidCharsRegex.test(filePath); +} + +/** + * Checks a directory for paths that are invalid in windows. + * @param currentDirectory {string} + * @return string[] + */ +function checkPaths(currentDirectory) { + /** @type {string[]} */ + const invalidPaths = []; + const files = fs.readdirSync(currentDirectory); + + files.forEach(file => { + const filePath = path.join(currentDirectory, file); + + if (containsInvalidCharactersForWindows(file)) { + // If new part of a path contains invalid characters, add the full relative path. + invalidPaths.push(filePath); + } + + if (fs.statSync(filePath).isDirectory()) { + invalidPaths.push(...checkPaths(filePath)); + } + }); + + return invalidPaths; +} + +// Main script +const currentDirectory = process.cwd(); +const invalidPaths = checkPaths(currentDirectory); + +if (invalidPaths.length > 0) { + console.log(`${invalidPaths.length} invalid path(s) for Windows found.`); + for (const invalidPath of invalidPaths) { + console.log(`- ${invalidPath}`) + } + process.exit(1); +} else { + console.log(`The path ${currentDirectory} only contains valid windows file names.`) +} diff --git a/tools/scripts/delete-dependencies.mjs b/tools/scripts/delete-dependencies.mjs new file mode 100644 index 00000000..97e91f28 --- /dev/null +++ b/tools/scripts/delete-dependencies.mjs @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + writePackageJson, parsePackageJson, getOutputPath +} from './shared-util.mjs'; + +// Executing this script: node path/to/delete-dependencies.mjs {projectName} +const [, , projectName] = process.argv; +process.chdir(getOutputPath(projectName)); + +const packageJson = parsePackageJson(); + +delete packageJson.dependencies; + +writePackageJson(packageJson); diff --git a/tools/scripts/docs/create-new-version-snapshot.mjs b/tools/scripts/docs/create-new-version-snapshot.mjs new file mode 100644 index 00000000..a0267fb1 --- /dev/null +++ b/tools/scripts/docs/create-new-version-snapshot.mjs @@ -0,0 +1,65 @@ +import fs from "fs"; +import path from 'path'; +import { execSync } from 'child_process'; + +import { + parsePackageJson, getSourcePath +} from '../shared-util.mjs'; +import { workspaceRoot } from "@nx/devkit"; + +// Executing this script: node path/to/prepare-new-version-snapshot.mjs + +const licenseHeader = "SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg" + + "\n\n" + + "SPDX-License-Identifier: AGPL-3.0-only"; + +process.chdir(workspaceRoot); +const packageJson = parsePackageJson(); +const versionTag = packageJson.version + +const pathToDocsVersion = path.join(getSourcePath('docs'), '..', 'versioned_docs', `version-${versionTag}`); +const pathToSidebarsVersion = path.join(getSourcePath('docs'), '..', 'versioned_sidebars'); + +createDocusaurusSnapshot(); +deleteGitignoreRecursively(pathToDocsVersion); +addLicenseHeaderToFilesRecursive(pathToDocsVersion) +addLicenseHeaderToFilesRecursive(pathToSidebarsVersion) + +function createDocusaurusSnapshot() { + const pathToDocsProject = getSourcePath('docs'); + process.chdir(path.join(pathToDocsProject, '..')) + execSync(`npx docusaurus docs:version ${versionTag}`); + console.log(`Created docs snapshot for version ${versionTag}`); + process.chdir(workspaceRoot); +} + +function deleteGitignoreRecursively(dir) { + for (const file of fs.readdirSync(dir)) { + const filePath = path.join(dir, file); + const fileStats = fs.statSync(filePath); + + if (fileStats.isDirectory()) { + deleteGitignoreRecursively(filePath); + } else if (file === '.gitignore') { + fs.unlinkSync(filePath); + console.log(`Deleted .gitignore file at ${filePath}`); + } + } +} + +function addLicenseHeaderToFilesRecursive(dir) { + for (const file of fs.readdirSync(dir)) { + const filePath = path.join(dir, file); + const fileStats = fs.statSync(filePath); + + if (fileStats.isDirectory()) { + addLicenseHeaderToFilesRecursive(filePath); + } else if (!file.endsWith('.license')){ + const licenseFilePath = path.join(dir, `${file}.license`) + if (!fs.existsSync(licenseFilePath)) { + fs.writeFileSync(licenseFilePath, licenseHeader) + console.log(`Created license file at ${filePath}`); + } + } + } +} \ No newline at end of file diff --git a/tools/scripts/docs/create-new-version-snapshot.mjs.license b/tools/scripts/docs/create-new-version-snapshot.mjs.license new file mode 100644 index 00000000..42737858 --- /dev/null +++ b/tools/scripts/docs/create-new-version-snapshot.mjs.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only \ No newline at end of file diff --git a/tools/scripts/fix-monarch-grammar-escape.mjs b/tools/scripts/fix-monarch-grammar-escape.mjs new file mode 100644 index 00000000..6eac8c08 --- /dev/null +++ b/tools/scripts/fix-monarch-grammar-escape.mjs @@ -0,0 +1,15 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import {getSourcePath} from "./shared-util.mjs"; +import {readFileSync, writeFileSync} from "fs"; + +// This script solely serves as a temporary workaround until https://github.com/langium/langium/issues/740 is resolved. + +const monarchFilePath = getSourcePath('monaco-editor') + '/lib/jayvee.monarch.ts'; + +const monarchFileContent = readFileSync(monarchFilePath).toString(); + +// Replace unescaped occurrence of '|/'with '|\/': +writeFileSync(monarchFilePath, monarchFileContent.replace(/\|\//g, '|\\/')); diff --git a/tools/scripts/interpreter/prepend-shebang.mjs b/tools/scripts/interpreter/prepend-shebang.mjs new file mode 100644 index 00000000..9e009c1a --- /dev/null +++ b/tools/scripts/interpreter/prepend-shebang.mjs @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { getOutputPath } from '../shared-util.mjs'; +import fs from 'fs'; + +// Executing this script: node path/to/prepend-shebang.mjs {projectName} {file} +const [, , projectName, file] = process.argv; +const shebang = '#!/usr/bin/env node'; + +process.chdir(getOutputPath(projectName)); + +console.log(`Prepending shebang to file ${process.cwd()}/${file}`); +const previousFileContent = fs.readFileSync(file); +fs.writeFileSync(file, `${shebang}\n${previousFileContent}`); +console.log(`Finished appending shebang!`); \ No newline at end of file diff --git a/tools/scripts/interpreter/rewrite-version-mainjs.mjs b/tools/scripts/interpreter/rewrite-version-mainjs.mjs new file mode 100644 index 00000000..e859582d --- /dev/null +++ b/tools/scripts/interpreter/rewrite-version-mainjs.mjs @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import {readFileSync, writeFileSync} from 'fs'; +import { + parsePackageJson, getOutputPath +} from '../shared-util.mjs'; +import { workspaceRoot } from "@nx/devkit"; + +// Executing this script: node path/to/rewrite-version-mainjs.mjs {projectName} +const [, , projectName] = process.argv; + +process.chdir(workspaceRoot); +const rootPackageJson = parsePackageJson(); + +process.chdir(getOutputPath(projectName)); + +const mainJs = readFileSync(`main.js`).toString(); +const modifiedMainJs = mainJs.replace('$REPLACE_WITH_PROGRAM_VERSION_IN_PREPUBLISH_STEP', rootPackageJson.version); +writeFileSync(`main.js`, modifiedMainJs); diff --git a/tools/scripts/language-server/generate-stdlib.mjs b/tools/scripts/language-server/generate-stdlib.mjs new file mode 100644 index 00000000..5f403580 --- /dev/null +++ b/tools/scripts/language-server/generate-stdlib.mjs @@ -0,0 +1,73 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only +//@ts-check + +import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "fs"; +import { getSourcePath } from "../shared-util.mjs"; +import { join } from "path"; + +const projectName = 'language-server'; +const stdLibInputPath = 'stdlib'; +const outputDirPath = join('lib', 'builtin-library', 'generated'); +const outputFilePath = join(outputDirPath, 'partial-stdlib.ts'); + +// Executing this script: node path/to/generate-stdlib.mjs +console.log('Generating stdlib...'); +const sourcePath = getSourcePath(projectName); +if (sourcePath === undefined) { + throw Error(`Project ${projectName} does not exist!`); +} +process.chdir(sourcePath); + +console.log(`Reading jv files from directory ${sourcePath}/${stdLibInputPath}`) +const libsArray = readLibrariesFromDirectory([stdLibInputPath]); +const libsRecord = {}; +libsArray.forEach(lib => { + libsRecord[lib.name] = lib.content; +}) + +console.log(`Writing standard lib to file ${sourcePath}/${outputFilePath}`) +const stdlibOutput = `/****************************************************************************** +* This file was generated by tools/scripts/language-server/generate-stdlib.mjs. +* DO NOT EDIT MANUALLY! +******************************************************************************/ + +` + 'export const PartialStdLib = ' + JSON.stringify(libsRecord, null, 2) + ';\n'; +if (!existsSync(outputDirPath)) { + mkdirSync(outputDirPath, {recursive: true}); +} +writeFileSync(outputFilePath, stdlibOutput, { encoding: 'utf8', flag: 'w'}); + +/** + * Recursively reads all libraries in the given directory. + * @param pathParts a list of the parts in the path + * @returns a list of all libraries with their names and their string content. + * Note that the name of a library follows this pattern: "builtin:///${filePath}" + */ +function readLibrariesFromDirectory(pathParts) { + const path = join(...pathParts); + const libs = []; + + readdirSync(path, { withFileTypes: true }) + .filter((file) => !file.isDirectory()) + .filter((file) => file.name.endsWith('.jv')) + .forEach((file) => { + const libName = file.name; + const libPath = `${path}/${libName}`; + const libContent = readFileSync(libPath); + libs.push({ + name: `builtin:///${libPath}`, + content: libContent.toString(), + }); + }); + + readdirSync(path, { withFileTypes: true }) + .filter((file) => file.isDirectory()) + .forEach((directory) => { + const libsOfSubdir = readLibrariesFromDirectory([path, directory.name]); + libs.push(...libsOfSubdir); + }); + + return libs; +} \ No newline at end of file diff --git a/tools/scripts/monaco-editor/delete-vscode-peer-dependency.mjs b/tools/scripts/monaco-editor/delete-vscode-peer-dependency.mjs new file mode 100644 index 00000000..1ecb838c --- /dev/null +++ b/tools/scripts/monaco-editor/delete-vscode-peer-dependency.mjs @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { getOutputPath, parsePackageJson, writePackageJson } from "../shared-util.mjs"; + +// Executing this script: node path/to/delete-vscode-peer-dependency.mjs {projectName} +const [, , projectName] = process.argv; +process.chdir(getOutputPath(projectName)); + +const packageJson = parsePackageJson(); + +/* + In our editor, we perform imports from a package called "vscode". + This is however not the true name of the package. + The actual name of the package is `@codingame/monaco-vscode-api`. + `monaco-languageclient` renames this package (for whatever reason), see + https://github.com/TypeFox/monaco-languageclient/blob/c5511b19e95e237c3f95a0fc0588769263f3ba40/packages/client/package.json#L56 + + This is a problem for our bundler, because it seems unable to detect that the package has been renamed. + Thus, it creates an entry in `package.json`, saying that our library depends on `vscode` instead of `@codingame/monaco-vscode-api`. + + Since this package is a peer dependency of `monaco-languageclient` anyways, we can simply remove the entry for `vscode` to fix the problem. +*/ +if (packageJson.peerDependencies) { + delete packageJson.peerDependencies.vscode; +} + +writePackageJson(packageJson); \ No newline at end of file diff --git a/tools/scripts/monaco-editor/relax-react-version.mjs b/tools/scripts/monaco-editor/relax-react-version.mjs new file mode 100644 index 00000000..b84a1766 --- /dev/null +++ b/tools/scripts/monaco-editor/relax-react-version.mjs @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { getOutputPath, parsePackageJson, writePackageJson } from "../shared-util.mjs"; + +// Executing this script: node path/to/relax-react-version.mjs {projectName} +const [, , projectName] = process.argv; +process.chdir(getOutputPath(projectName)); + +const packageJson = parsePackageJson(); + +// By default, this value is set to the exact React version we are using. This makes it hard to use the package in environments where a different React version is present. +if (packageJson.peerDependencies) { + packageJson.peerDependencies.react = '>= 17'; +} + +writePackageJson(packageJson); \ No newline at end of file diff --git a/tools/scripts/pack.mjs b/tools/scripts/pack.mjs new file mode 100644 index 00000000..c90680f2 --- /dev/null +++ b/tools/scripts/pack.mjs @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import {execSync} from 'child_process'; +import {getOutputPath} from "./shared-util.mjs"; + +// Executing this script: node path/to/pack.mjs {projectName} +const [, , projectName] = process.argv; + +process.chdir(getOutputPath(projectName)); + +execSync(`npm pack`); diff --git a/tools/scripts/publish.mjs b/tools/scripts/publish.mjs new file mode 100644 index 00000000..26a279d3 --- /dev/null +++ b/tools/scripts/publish.mjs @@ -0,0 +1,15 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { execSync } from 'child_process'; +import { getOutputPath } from './shared-util.mjs'; + +// Executing this script: node path/to/publish.mjs {projectName} {no-dry-run} +const [, , projectName, isNoDryRun] = process.argv; + +const isDryRun = isNoDryRun !== 'true'; + +const outputPath = getOutputPath(projectName); + +execSync(`npm publish ${outputPath} ${!isDryRun ? '' : '--dry-run'}`); diff --git a/tools/scripts/relax-peer-dependency-versions.mjs b/tools/scripts/relax-peer-dependency-versions.mjs new file mode 100644 index 00000000..116766a1 --- /dev/null +++ b/tools/scripts/relax-peer-dependency-versions.mjs @@ -0,0 +1,50 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { + invariant, + parsePackageVersion, + writePackageJson, + parsePackageJson, + getOutputPath +} from './shared-util.mjs'; + +/** + * This script applies the following changes to the peer dependencies: + * - If a package version like "1.2.3" is specified, it gets rewritten to "^1.0.0". + * - A special case of the previous one: If a package version like "0.1.2" is specified, then this version gets rewritten to "^0.1.0". This is because work-in-progress packages are often versioned in a way that minor releases indicate larger changes. + * + * Summary: + * + * ``` txt + * "foo": "1.2.3" + * --> "foo": "^1.0.0" + * + * "foo": "0.1.2" + * --> foo": "^0.1.0" + * ``` + */ + +// Executing this script: node path/to/relax-peer-dependency-versions.mjs {projectName} +const [, , projectName] = process.argv; +process.chdir(getOutputPath(projectName)); + +const packageJson = parsePackageJson(); + +if (packageJson.peerDependencies) { + for (const [packageName, packageVersion] of Object.entries( + packageJson.peerDependencies, + )) { + const parsedVersion = parsePackageVersion(packageVersion); + let newVersion; + if (parsedVersion.major < 1) { + newVersion = `^0.${parsedVersion.minor}.0`; + } else { + newVersion = `^${parsedVersion.major}.0.0`; + } + packageJson.peerDependencies[packageName] = newVersion; + } +} + +writePackageJson(packageJson); diff --git a/tools/scripts/shared-util.mjs b/tools/scripts/shared-util.mjs new file mode 100644 index 00000000..baf86328 --- /dev/null +++ b/tools/scripts/shared-util.mjs @@ -0,0 +1,89 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import {readFileSync, writeFileSync} from 'fs'; +import {readCachedProjectGraph} from "@nx/devkit"; +import chalk from "chalk"; + +export function invariant(condition, message) { + if (!condition) { + console.error(chalk.bold.red(message)); + process.exit(1); + } +} + +export function getOutputPath(projectName) { + invariant( + projectName, + `No project name was provided.`, + ); + + const graph = readCachedProjectGraph(); + const project = graph.nodes[projectName]; + + invariant( + project, + `Could not find project "${projectName}" in the workspace. Is the project.json configured correctly?`, + ); + + const outputPath = project.data?.targets?.build?.options?.outputPath; + invariant( + outputPath, + `Could not find "build.options.outputPath" of project "${projectName}". Is project.json configured correctly?`, + ); + + return outputPath; +} + +export function getSourcePath(projectName) { + invariant( + projectName, + `No project name was provided.`, + ); + + const graph = readCachedProjectGraph(); + const project = graph.nodes[projectName]; + + invariant( + project, + `Could not find project "${projectName}" in the workspace. Is the project.json configured correctly?`, + ); + + const sourcePath = project.data?.sourceRoot; + invariant( + sourcePath, + `Could not find "sourceRoot" of project "${projectName}". Is project.json configured correctly?`, + ); + + return sourcePath; +} + +export function parsePackageJson() { + return JSON.parse(readFileSync(`package.json`).toString()); +} + +export function writePackageJson(parsedContent) { + invariant( + typeof parsedContent === 'object', + `The content for package.json is expected to be an object.`, + ); + + writeFileSync(`package.json`, JSON.stringify(parsedContent, null, 2)); +} + +export function parsePackageVersion(versionString) { + const validVersion = /^(\d+)\.(\d+)\.(\d+)(-\w+\.\d+)?/; + invariant( + versionString && validVersion.test(versionString), + `Versions are expected to match Semantic Versioning, expected: #.#.#-tag.# or #.#.#, got ${versionString}.`, + ); + + const [, major, minor, patch] = validVersion.exec(versionString); + + return { + major: Number.parseInt(major, 10), + minor: Number.parseInt(minor, 10), + patch: Number.parseInt(patch, 10), + }; +} diff --git a/tsconfig.base.json b/tsconfig.base.json new file mode 100644 index 00000000..e586a062 --- /dev/null +++ b/tsconfig.base.json @@ -0,0 +1,50 @@ +{ + "compilerOptions": { + "rootDir": ".", + "sourceMap": true, + "declaration": false, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "target": "ES2015", + "module": "ESNext", + "moduleResolution": "Bundler", + "lib": ["ES2020"], + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "skipLibCheck": true, + "skipDefaultLibCheck": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noImplicitOverride": true, + "noUncheckedIndexedAccess": true, + "exactOptionalPropertyTypes": true, + "noFallthroughCasesInSwitch": true, + "strictNullChecks": true, + "baseUrl": ".", + "paths": { + "@jvalue/jayvee-execution": ["libs/execution/src/index.ts"], + "@jvalue/jayvee-execution/test": ["libs/execution/test/index.ts"], + "@jvalue/jayvee-extensions/rdbms/exec": [ + "libs/extensions/rdbms/exec/src/index.ts" + ], + "@jvalue/jayvee-extensions/rdbms/test": [ + "libs/extensions/rdbms/exec/test/index.ts" + ], + "@jvalue/jayvee-extensions/std/exec": [ + "libs/extensions/std/exec/src/index.ts" + ], + "@jvalue/jayvee-extensions/std/test": [ + "libs/extensions/std/exec/test/index.ts" + ], + "@jvalue/jayvee-extensions/tabular/exec": [ + "libs/extensions/tabular/exec/src/index.ts" + ], + "@jvalue/jayvee-interpreter-lib": ["libs/interpreter-lib/src/index.ts"], + "@jvalue/jayvee-language-server": ["libs/language-server/src/index.ts"], + "@jvalue/jayvee-language-server/test": [ + "libs/language-server/src/test/index.ts" + ], + "@jvalue/jayvee-monaco": ["libs/monaco-editor/src/index.ts"] + } + } +} diff --git a/tsconfig.base.json.license b/tsconfig.base.json.license new file mode 100644 index 00000000..17c5d2ba --- /dev/null +++ b/tsconfig.base.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/vitest.workspace.ts b/vitest.workspace.ts new file mode 100644 index 00000000..7eea0b66 --- /dev/null +++ b/vitest.workspace.ts @@ -0,0 +1,5 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +export default ['**/*/vite.config.ts', '**/*/vitest.config.ts'];