diff --git a/.github/workflows/cd.yaml b/.github/workflows/cd.yaml index 9b4a3d6..9a45d7b 100644 --- a/.github/workflows/cd.yaml +++ b/.github/workflows/cd.yaml @@ -17,7 +17,7 @@ jobs: needs: ci-check-python steps: - name: Checkout source code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Tag release id: tag diff --git a/.github/workflows/check-python.yaml b/.github/workflows/check-python.yaml index 82d7b49..32c7eea 100644 --- a/.github/workflows/check-python.yaml +++ b/.github/workflows/check-python.yaml @@ -8,18 +8,18 @@ jobs: runs-on: 'ubuntu-latest' steps: - name: Checkout source code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install poetry run: pipx install poetry - name: Install algokit - run: pipx install algokit + run: pipx install git+https://github.com/algorandfoundation/algokit-cli@feat/init_wizard_v2 --force - - name: Set up Python 3.10 - uses: actions/setup-python@v4 + - name: Set up Python 3.12 + uses: actions/setup-python@v5 with: - python-version: '3.10' + python-version: '3.12' cache: 'poetry' - name: Install dependencies @@ -51,6 +51,6 @@ jobs: shell: bash run: | # Add untracked files as empty so they come up in diff - git add -N ./tests_generated + git add -N ./examples # Look for changes in generated templates and error if there are any - git diff --exit-code --minimal ./tests_generated ':(exclude)package-lock.json' + git diff --exit-code --minimal ./examples ':(exclude)package-lock.json' diff --git a/.github/workflows/issue_closed.yml b/.github/workflows/issue_closed.yml deleted file mode 100644 index 80399ea..0000000 --- a/.github/workflows/issue_closed.yml +++ /dev/null @@ -1,12 +0,0 @@ -name: Solve zendesk ticket when the issue is closed -on: - issues: - types: [closed] -jobs: - issue_closed: - uses: algorandfoundation/gh_zendesk_sync/.github/workflows/github_zendesk_issue_closed.yml@main - with: - ZENDESK_TENANT_NAME: ${{ vars.ZENDESK_TENANT_NAME }} - ISSUE_LABEL: makerx - secrets: - ZENDESK_AUTH_TOKEN: ${{ secrets.ZENDESK_AUTH_TOKEN }} diff --git a/.github/workflows/issue_commented.yml b/.github/workflows/issue_commented.yml deleted file mode 100644 index 17c7c29..0000000 --- a/.github/workflows/issue_commented.yml +++ /dev/null @@ -1,12 +0,0 @@ -name: Add comment to zendesk ticket on GitHub issue commented -on: - issue_comment: - types: [created] -jobs: - issue_closed: - uses: algorandfoundation/gh_zendesk_sync/.github/workflows/github_zendesk_issue_commented.yml@main - with: - ZENDESK_TENANT_NAME: ${{ vars.ZENDESK_TENANT_NAME }} - ISSUE_LABEL: makerx - secrets: - ZENDESK_AUTH_TOKEN: ${{ secrets.ZENDESK_AUTH_TOKEN }} diff --git a/.github/workflows/issue_labelled.yml b/.github/workflows/issue_labelled.yml deleted file mode 100644 index 950ea3e..0000000 --- a/.github/workflows/issue_labelled.yml +++ /dev/null @@ -1,12 +0,0 @@ -name: Create Zendesk ticket when an issue is labelled with makerx -on: - issues: - types: [labeled] -jobs: - issue_created: - uses: algorandfoundation/gh_zendesk_sync/.github/workflows/github_zendesk_issue_labelled.yml@main - with: - ZENDESK_TENANT_NAME: ${{ vars.ZENDESK_TENANT_NAME }} - ISSUE_LABEL: makerx - secrets: - ZENDESK_AUTH_TOKEN: ${{ secrets.ZENDESK_AUTH_TOKEN }} diff --git a/.github/workflows/zendesk_github_add_comment.yml b/.github/workflows/zendesk_github_add_comment.yml deleted file mode 100644 index 07f0c1b..0000000 --- a/.github/workflows/zendesk_github_add_comment.yml +++ /dev/null @@ -1,11 +0,0 @@ -name: Add comment to GitHub issue on Zendesk ticket commented -on: - repository_dispatch: - types: - - zendesk_github_add_comment -permissions: - issues: write -jobs: - add-comment: - name: Add comment to issue - uses: algorandfoundation/gh_zendesk_sync/.github/workflows/zendesk_github_add_comment.yml@main diff --git a/.github/workflows/zendesk_github_close_issue.yml b/.github/workflows/zendesk_github_close_issue.yml deleted file mode 100644 index 42d33d7..0000000 --- a/.github/workflows/zendesk_github_close_issue.yml +++ /dev/null @@ -1,11 +0,0 @@ -name: Close GitHub issue on Zendesk ticket solved -on: - repository_dispatch: - types: - - zendesk_github_close_issue -permissions: - issues: write -jobs: - close_issue: - name: Close GitHub issue - uses: algorandfoundation/gh_zendesk_sync/.github/workflows/zendesk_github_close_issue.yml@main diff --git a/.gitignore b/.gitignore index ddf6ffb..fe232bd 100644 --- a/.gitignore +++ b/.gitignore @@ -170,5 +170,10 @@ cython_debug/ # NPM node_modules -# Ignore package-lock.json in all directories and subdirectories under tests_generated -tests_generated/*/package-lock.json +# Ignore lockfiles in all directories and subdirectories under examples, recursively +examples/**/package-lock.json +examples/**/poetry.lock + +# playground folder for previewing templates +.playground/* +!.playground/.gitkeep diff --git a/.playground/.gitkeep b/.playground/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..6a13bbd --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,40 @@ +# AlgoKit Official Template for contributors + +This repository is a template for creating new AlgoKit projects. It includes a basic structure for creating a front end project. + +## Pre-requisites + +`poetry install` - Install the dependencies for the project. + +## Testing + +```bash +poetry run pytest +``` + +This will regenerate the tests for default `starter` and `production` presets as well as default tests for `generators` available on the template. + +## Development + +```bash +poetry run copier copy . .playground/{some_dummy_folder_name} --vcs-ref=HEAD --trust +``` + +To generate a dummy project into the `.playground` folder. This is useful for testing the template to quickly preview the output of the template before testing via `pytest`. + +## Contributing + +### Commits + +We are using the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/#summary) standard for commit messages. This allows us to automatically generate release notes and version numbers. We do this via [Python Semantic Release](https://python-semantic-release.readthedocs.io/en/latest/) and [GitHub actions](.github/workflows/cd.yaml). + +### Guiding Principles + +AlgoKit development is done within the [AlgoKit Guiding Principles](https://github.com/algorandfoundation/algokit-cli/blob/main/docs/algokit.md#guiding-principles). + +### Pull Requests + +1. Submit a pull request to the `main` branch. Fork the repo if you are an external contributor. +2. Ensure that the pull request is up to date with the `main` branch. +3. Ensure that the pull request has a clear title and description. +4. Pass PR reviews and wait for approval. diff --git a/README.md b/README.md index 4c2ba20..b358bce 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ - +

--- @@ -30,6 +30,20 @@ This template supports the following features: - Dotenv support for environment variables, as well as a local only KMD provider that can be used for connecting the frontend component to an `algokit localnet` instance (docker required). - CI/CD pipeline using GitHub Actions (Vercel or Netlify for hosting) -# Getting started +## Getting started Once the template is instantiated you can follow the [README.md](template_content/README.md.jinja) file to see instructions for how to use the template. + +### Interactive Wizard + +**To initialize using the `algokit` CLI**: + +- Execute the command `algokit init`. This initiates an interactive wizard that assists in selecting the most appropriate template for your project requirements. + +**To initialize within GitHub Codespaces**: + +- Follow these steps to leverage GitHub Codespaces for template selection: + + 1. Go to the [algokit-base-template](https://github.com/algorandfoundation/algokit-base-template) repository. + 2. Initiate a new codespace by selecting the `Create codespace on main` option. This can be found by clicking the `Code` button, then navigating to the `Codespaces` tab. + 3. Upon codespace preparation, `algokit` will automatically start `LocalNet` and present a prompt with the next steps. Executing `algokit init` will initiate the interactive wizard. diff --git a/copier.yaml b/copier.yaml index a6a669f..040d741 100644 --- a/copier.yaml +++ b/copier.yaml @@ -1,4 +1,5 @@ _subdirectory: template_content +_templates_suffix: '.jinja' # questions # project_name should never get prompted, AlgoKit should always pass it by convention @@ -17,47 +18,62 @@ author_email: help: Package author email placeholder: 'your@email.tld' +preset_name: + type: str + help: Name of the template preset to use. + choices: + 'Starter - for a simpler starting point ideal for prototyping': 'starter' + 'Production - for confidently deploying to MainNet and/or more complex projects': 'production' + 'Custom - for tailoring the template output to your needs': 'custom' + default: 'starter' + ide_vscode: type: bool help: Do you want to add VSCode configuration? + when: "{{ preset_name == 'custom' }}" default: yes ide_jetbrains: type: bool help: Do you want to add JetBrains configuration (primarily optimized for WebStorm)? - when: '{{ ide_vscode == false }}' - default: no + when: "{{ preset_name == 'custom' }}" + default: "{{ 'yes' if preset_name == 'production' else 'no' }}" use_eslint_prettier: type: bool help: Do you want to use ESLint and Prettier for code linting and formatting? - default: yes + when: "{{ preset_name == 'custom' }}" + default: "{{ 'yes' if preset_name == 'production' else 'no' }}" use_tailwind: type: bool help: Do you want to use Tailwind CSS? A utility-first CSS framework for rapidly building custom designs. - default: yes + when: "{{ preset_name == 'custom' }}" + default: "{{ 'yes' if preset_name == 'production' else 'no' }}" use_daisy_ui: type: bool - help: Do you want to use a daisyUI? Framework agnostic CSS component library for building modern websites and web applications fast. - default: yes - when: '{{ use_tailwind != false }}' + help: Do you want to use daisyUI? Framework agnostic CSS component library for building modern websites and web applications fast. + when: "{{ use_tailwind != false and preset_name == 'custom' }}" + default: "{{ 'yes' if preset_name == 'production' else 'no' }}" use_jest: type: bool help: Do you want to include unit tests (via Jest)? - default: yes + when: "{{ preset_name == 'custom' }}" + default: "{{ 'yes' if preset_name == 'production' else 'no' }}" use_playwright: type: bool help: Do you want to include end to end tests (via Playwright)? - default: yes + when: "{{ preset_name == 'custom' }}" + default: "{{ 'yes' if preset_name == 'production' else 'no' }}" use_github_actions: type: bool help: Do you want to include Github Actions workflows for build validation? - default: yes + when: "{{ preset_name == 'custom' }}" + default: "{{ 'yes' if preset_name == 'production' else 'no' }}" cloud_provider: type: str @@ -67,36 +83,4 @@ cloud_provider: Netlify: 'netlify' Vercel: 'vercel' Skip CD setup: 'none' - default: 'netlify' - -# The following should never get prompted; algokit should always pass these values through by convention - -algod_token: - type: str - help: Default Algod Token - default: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' - -algod_server: - type: str - help: Default Algod server - default: 'http://localhost' - -algod_port: - type: int - help: Default Algod port - default: 4001 - -indexer_token: - type: str - help: Default Indexer token - default: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' - -indexer_server: - type: str - help: Default Indexer server - default: 'http://localhost' - -indexer_port: - type: int - help: Default Indexer port - default: 8980 + default: "{{ 'netlify' if preset_name == 'production' else 'none' }}" diff --git a/examples/.gitkeep b/examples/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/examples/cloud_provider/.gitkeep b/examples/cloud_provider/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/examples/cloud_provider/production_react_netlify/.algokit.toml b/examples/cloud_provider/production_react_netlify/.algokit.toml new file mode 100644 index 0000000..754e2a4 --- /dev/null +++ b/examples/cloud_provider/production_react_netlify/.algokit.toml @@ -0,0 +1,10 @@ +[algokit] +min_version = "v1.3.0b1" + +[generate.import_contract] +description = "Import a typed client from your smart contracts project" +path = ".algokit/generators/import_contract" + +[project] +type = "frontend" +name = "production_react_netlify" diff --git a/examples/cloud_provider/production_react_netlify/.copier-answers.yml b/examples/cloud_provider/production_react_netlify/.copier-answers.yml new file mode 100644 index 0000000..19a9f50 --- /dev/null +++ b/examples/cloud_provider/production_react_netlify/.copier-answers.yml @@ -0,0 +1,9 @@ +# Changes here will be overwritten by Copier; NEVER EDIT MANUALLY +_commit: +_src_path: +author_email: None +author_name: None +cloud_provider: netlify +preset_name: production +project_name: production_react_netlify + diff --git a/tests_generated/test_all_default_parameters_on_netlify/.editorconfig b/examples/cloud_provider/production_react_netlify/.editorconfig similarity index 100% rename from tests_generated/test_all_default_parameters_on_netlify/.editorconfig rename to examples/cloud_provider/production_react_netlify/.editorconfig diff --git a/tests_generated/test_all_default_parameters_jetbrains/.env.template b/examples/cloud_provider/production_react_netlify/.env.template similarity index 100% rename from tests_generated/test_all_default_parameters_jetbrains/.env.template rename to examples/cloud_provider/production_react_netlify/.env.template diff --git a/tests_generated/test_all_default_parameters_on_netlify/.eslintrc b/examples/cloud_provider/production_react_netlify/.eslintrc similarity index 100% rename from tests_generated/test_all_default_parameters_on_netlify/.eslintrc rename to examples/cloud_provider/production_react_netlify/.eslintrc diff --git a/tests_generated/test_all_default_parameters_jetbrains/.gitattributes b/examples/cloud_provider/production_react_netlify/.gitattributes similarity index 100% rename from tests_generated/test_all_default_parameters_jetbrains/.gitattributes rename to examples/cloud_provider/production_react_netlify/.gitattributes diff --git a/examples/cloud_provider/production_react_netlify/.github/workflows/checks.yaml b/examples/cloud_provider/production_react_netlify/.github/workflows/checks.yaml new file mode 100644 index 0000000..4723b1a --- /dev/null +++ b/examples/cloud_provider/production_react_netlify/.github/workflows/checks.yaml @@ -0,0 +1,54 @@ +name: Check code base + +on: + workflow_call: + inputs: + run-build: + required: false + type: boolean + default: false + push: + branches: + - main + +jobs: + checks: + runs-on: 'ubuntu-latest' + steps: + - name: Check out repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup node + uses: actions/setup-node@v3 + with: + node-version: 18 + + - name: Install dependencies + run: npm ci + + - name: Run linters + run: npm run lint + + - name: Run unit tests + run: npm run test + + - name: Create placeholder .env file + if: ${{ inputs.run-build }} + uses: makerxstudio/shared-config/.github/actions/env-to-placeholders@main + with: + env-output-path: './.env' + env-template-path: './.env.template' + env-variable-prefix: VITE_ + + - name: Build + if: ${{ inputs.run-build }} + run: npm run build + + - name: Archive + if: ${{ inputs.run-build }} + uses: actions/upload-artifact@v4 + with: + name: dist + path: dist/ diff --git a/tests_generated/test_all_default_parameters_on_netlify/.github/workflows/pr.yaml b/examples/cloud_provider/production_react_netlify/.github/workflows/pr.yaml similarity index 100% rename from tests_generated/test_all_default_parameters_on_netlify/.github/workflows/pr.yaml rename to examples/cloud_provider/production_react_netlify/.github/workflows/pr.yaml diff --git a/tests_generated/test_all_default_parameters_on_netlify/.github/workflows/release.yaml b/examples/cloud_provider/production_react_netlify/.github/workflows/release.yaml similarity index 100% rename from tests_generated/test_all_default_parameters_on_netlify/.github/workflows/release.yaml rename to examples/cloud_provider/production_react_netlify/.github/workflows/release.yaml diff --git a/tests_generated/test_all_default_parameters_jetbrains/.gitignore b/examples/cloud_provider/production_react_netlify/.gitignore similarity index 100% rename from tests_generated/test_all_default_parameters_jetbrains/.gitignore rename to examples/cloud_provider/production_react_netlify/.gitignore diff --git a/tests_generated/test_all_default_parameters_jetbrains/.idea/runConfigurations/Run_Chrome.xml b/examples/cloud_provider/production_react_netlify/.idea/runConfigurations/Run_Chrome.xml similarity index 100% rename from tests_generated/test_all_default_parameters_jetbrains/.idea/runConfigurations/Run_Chrome.xml rename to examples/cloud_provider/production_react_netlify/.idea/runConfigurations/Run_Chrome.xml diff --git a/tests_generated/test_all_default_parameters_jetbrains/.idea/runConfigurations/Run_dApp.xml b/examples/cloud_provider/production_react_netlify/.idea/runConfigurations/Run_dApp.xml similarity index 100% rename from tests_generated/test_all_default_parameters_jetbrains/.idea/runConfigurations/Run_dApp.xml rename to examples/cloud_provider/production_react_netlify/.idea/runConfigurations/Run_dApp.xml diff --git a/tests_generated/test_all_default_parameters_jetbrains/.idea/runConfigurations/Run_dApp____LocalNet_.xml b/examples/cloud_provider/production_react_netlify/.idea/runConfigurations/Run_dApp____LocalNet_.xml similarity index 100% rename from tests_generated/test_all_default_parameters_jetbrains/.idea/runConfigurations/Run_dApp____LocalNet_.xml rename to examples/cloud_provider/production_react_netlify/.idea/runConfigurations/Run_dApp____LocalNet_.xml diff --git a/tests_generated/test_all_default_parameters_jetbrains/.idea/runConfigurations/Start_AlgoKit_LocalNet.xml b/examples/cloud_provider/production_react_netlify/.idea/runConfigurations/Start_AlgoKit_LocalNet.xml similarity index 100% rename from tests_generated/test_all_default_parameters_jetbrains/.idea/runConfigurations/Start_AlgoKit_LocalNet.xml rename to examples/cloud_provider/production_react_netlify/.idea/runConfigurations/Start_AlgoKit_LocalNet.xml diff --git a/tests_generated/test_all_default_parameters_on_netlify/.prettierignore b/examples/cloud_provider/production_react_netlify/.prettierignore similarity index 100% rename from tests_generated/test_all_default_parameters_on_netlify/.prettierignore rename to examples/cloud_provider/production_react_netlify/.prettierignore diff --git a/tests_generated/test_all_default_parameters_on_netlify/.prettierrc.cjs b/examples/cloud_provider/production_react_netlify/.prettierrc.cjs similarity index 100% rename from tests_generated/test_all_default_parameters_on_netlify/.prettierrc.cjs rename to examples/cloud_provider/production_react_netlify/.prettierrc.cjs diff --git a/tests_generated/test_all_default_parameters_on_netlify/.vscode/extensions.json b/examples/cloud_provider/production_react_netlify/.vscode/extensions.json similarity index 100% rename from tests_generated/test_all_default_parameters_on_netlify/.vscode/extensions.json rename to examples/cloud_provider/production_react_netlify/.vscode/extensions.json diff --git a/tests_generated/test_all_default_parameters_on_netlify/.vscode/launch.json b/examples/cloud_provider/production_react_netlify/.vscode/launch.json similarity index 100% rename from tests_generated/test_all_default_parameters_on_netlify/.vscode/launch.json rename to examples/cloud_provider/production_react_netlify/.vscode/launch.json diff --git a/tests_generated/test_all_default_parameters_on_netlify/.vscode/settings.json b/examples/cloud_provider/production_react_netlify/.vscode/settings.json similarity index 100% rename from tests_generated/test_all_default_parameters_on_netlify/.vscode/settings.json rename to examples/cloud_provider/production_react_netlify/.vscode/settings.json diff --git a/tests_generated/test_all_default_parameters_on_netlify/.vscode/tasks.json b/examples/cloud_provider/production_react_netlify/.vscode/tasks.json similarity index 100% rename from tests_generated/test_all_default_parameters_on_netlify/.vscode/tasks.json rename to examples/cloud_provider/production_react_netlify/.vscode/tasks.json diff --git a/tests_generated/test_all_default_parameters_jetbrains/LICENSE b/examples/cloud_provider/production_react_netlify/LICENSE similarity index 100% rename from tests_generated/test_all_default_parameters_jetbrains/LICENSE rename to examples/cloud_provider/production_react_netlify/LICENSE diff --git a/tests_generated/test_all_default_parameters_on_vercel/README.md b/examples/cloud_provider/production_react_netlify/README.md similarity index 99% rename from tests_generated/test_all_default_parameters_on_vercel/README.md rename to examples/cloud_provider/production_react_netlify/README.md index 4bb6824..4ba1c7e 100644 --- a/tests_generated/test_all_default_parameters_on_vercel/README.md +++ b/examples/cloud_provider/production_react_netlify/README.md @@ -1,4 +1,4 @@ -# test_all_default_parameters_on_vercel +# production_react_netlify This starter React project has been generated using AlgoKit. See below for default getting started instructions. diff --git a/tests_generated/test_all_default_parameters_jetbrains/index.html b/examples/cloud_provider/production_react_netlify/index.html similarity index 100% rename from tests_generated/test_all_default_parameters_jetbrains/index.html rename to examples/cloud_provider/production_react_netlify/index.html diff --git a/tests_generated/test_all_default_parameters_on_netlify/jest.config.ts b/examples/cloud_provider/production_react_netlify/jest.config.ts similarity index 100% rename from tests_generated/test_all_default_parameters_on_netlify/jest.config.ts rename to examples/cloud_provider/production_react_netlify/jest.config.ts diff --git a/tests_generated/test_all_default_parameters_on_vercel/package.json b/examples/cloud_provider/production_react_netlify/package.json similarity index 97% rename from tests_generated/test_all_default_parameters_on_vercel/package.json rename to examples/cloud_provider/production_react_netlify/package.json index e475f48..217bd77 100644 --- a/tests_generated/test_all_default_parameters_on_vercel/package.json +++ b/examples/cloud_provider/production_react_netlify/package.json @@ -1,5 +1,5 @@ { - "name": "test_all_default_parameters_on_vercel", + "name": "production_react_netlify", "version": "0.1.0", "author": { "name": "None", diff --git a/tests_generated/test_all_default_parameters_on_netlify/playwright.config.ts b/examples/cloud_provider/production_react_netlify/playwright.config.ts similarity index 100% rename from tests_generated/test_all_default_parameters_on_netlify/playwright.config.ts rename to examples/cloud_provider/production_react_netlify/playwright.config.ts diff --git a/tests_generated/test_all_default_parameters_on_netlify/postcss.config.cjs b/examples/cloud_provider/production_react_netlify/postcss.config.cjs similarity index 100% rename from tests_generated/test_all_default_parameters_on_netlify/postcss.config.cjs rename to examples/cloud_provider/production_react_netlify/postcss.config.cjs diff --git a/tests_generated/test_all_default_parameters_jetbrains/public/index.html b/examples/cloud_provider/production_react_netlify/public/index.html similarity index 100% rename from tests_generated/test_all_default_parameters_jetbrains/public/index.html rename to examples/cloud_provider/production_react_netlify/public/index.html diff --git a/tests_generated/test_all_default_parameters_jetbrains/public/robots.txt b/examples/cloud_provider/production_react_netlify/public/robots.txt similarity index 100% rename from tests_generated/test_all_default_parameters_jetbrains/public/robots.txt rename to examples/cloud_provider/production_react_netlify/public/robots.txt diff --git a/tests_generated/test_all_default_parameters_jetbrains/src/App.tsx b/examples/cloud_provider/production_react_netlify/src/App.tsx similarity index 100% rename from tests_generated/test_all_default_parameters_jetbrains/src/App.tsx rename to examples/cloud_provider/production_react_netlify/src/App.tsx diff --git a/tests_generated/test_all_default_parameters_jetbrains/src/Home.tsx b/examples/cloud_provider/production_react_netlify/src/Home.tsx similarity index 100% rename from tests_generated/test_all_default_parameters_jetbrains/src/Home.tsx rename to examples/cloud_provider/production_react_netlify/src/Home.tsx diff --git a/tests_generated/test_all_default_parameters_jetbrains/src/assets/logo.svg b/examples/cloud_provider/production_react_netlify/src/assets/logo.svg similarity index 100% rename from tests_generated/test_all_default_parameters_jetbrains/src/assets/logo.svg rename to examples/cloud_provider/production_react_netlify/src/assets/logo.svg diff --git a/tests_generated/test_all_default_parameters_jetbrains/src/components/Account.tsx b/examples/cloud_provider/production_react_netlify/src/components/Account.tsx similarity index 100% rename from tests_generated/test_all_default_parameters_jetbrains/src/components/Account.tsx rename to examples/cloud_provider/production_react_netlify/src/components/Account.tsx diff --git a/tests_generated/test_all_default_parameters_on_netlify/src/components/ConnectWallet.tsx b/examples/cloud_provider/production_react_netlify/src/components/ConnectWallet.tsx similarity index 100% rename from tests_generated/test_all_default_parameters_on_netlify/src/components/ConnectWallet.tsx rename to examples/cloud_provider/production_react_netlify/src/components/ConnectWallet.tsx diff --git a/tests_generated/test_all_default_parameters_jetbrains/src/components/ErrorBoundary.tsx b/examples/cloud_provider/production_react_netlify/src/components/ErrorBoundary.tsx similarity index 100% rename from tests_generated/test_all_default_parameters_jetbrains/src/components/ErrorBoundary.tsx rename to examples/cloud_provider/production_react_netlify/src/components/ErrorBoundary.tsx diff --git a/tests_generated/test_all_default_parameters_on_netlify/src/components/Transact.tsx b/examples/cloud_provider/production_react_netlify/src/components/Transact.tsx similarity index 100% rename from tests_generated/test_all_default_parameters_on_netlify/src/components/Transact.tsx rename to examples/cloud_provider/production_react_netlify/src/components/Transact.tsx diff --git a/tests_generated/test_all_default_parameters_jetbrains/src/contracts/README.md b/examples/cloud_provider/production_react_netlify/src/contracts/README.md similarity index 100% rename from tests_generated/test_all_default_parameters_jetbrains/src/contracts/README.md rename to examples/cloud_provider/production_react_netlify/src/contracts/README.md diff --git a/tests_generated/test_all_default_parameters_jetbrains/src/interfaces/network.ts b/examples/cloud_provider/production_react_netlify/src/interfaces/network.ts similarity index 100% rename from tests_generated/test_all_default_parameters_jetbrains/src/interfaces/network.ts rename to examples/cloud_provider/production_react_netlify/src/interfaces/network.ts diff --git a/tests_generated/test_all_default_parameters_on_netlify/src/main.tsx b/examples/cloud_provider/production_react_netlify/src/main.tsx similarity index 100% rename from tests_generated/test_all_default_parameters_on_netlify/src/main.tsx rename to examples/cloud_provider/production_react_netlify/src/main.tsx diff --git a/tests_generated/test_all_default_parameters_on_netlify/src/styles/main.css b/examples/cloud_provider/production_react_netlify/src/styles/main.css similarity index 100% rename from tests_generated/test_all_default_parameters_on_netlify/src/styles/main.css rename to examples/cloud_provider/production_react_netlify/src/styles/main.css diff --git a/tests_generated/test_all_default_parameters_on_netlify/src/utils/ellipseAddress.spec.tsx b/examples/cloud_provider/production_react_netlify/src/utils/ellipseAddress.spec.tsx similarity index 100% rename from tests_generated/test_all_default_parameters_on_netlify/src/utils/ellipseAddress.spec.tsx rename to examples/cloud_provider/production_react_netlify/src/utils/ellipseAddress.spec.tsx diff --git a/tests_generated/test_all_default_parameters_jetbrains/src/utils/ellipseAddress.ts b/examples/cloud_provider/production_react_netlify/src/utils/ellipseAddress.ts similarity index 100% rename from tests_generated/test_all_default_parameters_jetbrains/src/utils/ellipseAddress.ts rename to examples/cloud_provider/production_react_netlify/src/utils/ellipseAddress.ts diff --git a/tests_generated/test_all_default_parameters_jetbrains/src/utils/network/getAlgoClientConfigs.ts b/examples/cloud_provider/production_react_netlify/src/utils/network/getAlgoClientConfigs.ts similarity index 100% rename from tests_generated/test_all_default_parameters_jetbrains/src/utils/network/getAlgoClientConfigs.ts rename to examples/cloud_provider/production_react_netlify/src/utils/network/getAlgoClientConfigs.ts diff --git a/tests_generated/test_all_default_parameters_jetbrains/src/vite-env.d.ts b/examples/cloud_provider/production_react_netlify/src/vite-env.d.ts similarity index 100% rename from tests_generated/test_all_default_parameters_jetbrains/src/vite-env.d.ts rename to examples/cloud_provider/production_react_netlify/src/vite-env.d.ts diff --git a/tests_generated/test_all_default_parameters_on_netlify/tailwind.config.js b/examples/cloud_provider/production_react_netlify/tailwind.config.js similarity index 100% rename from tests_generated/test_all_default_parameters_on_netlify/tailwind.config.js rename to examples/cloud_provider/production_react_netlify/tailwind.config.js diff --git a/tests_generated/test_all_default_parameters_on_netlify/tests/example.spec.ts b/examples/cloud_provider/production_react_netlify/tests/example.spec.ts similarity index 100% rename from tests_generated/test_all_default_parameters_on_netlify/tests/example.spec.ts rename to examples/cloud_provider/production_react_netlify/tests/example.spec.ts diff --git a/tests_generated/test_all_default_parameters_on_netlify/tsconfig.json b/examples/cloud_provider/production_react_netlify/tsconfig.json similarity index 100% rename from tests_generated/test_all_default_parameters_on_netlify/tsconfig.json rename to examples/cloud_provider/production_react_netlify/tsconfig.json diff --git a/tests_generated/test_all_default_parameters_jetbrains/tsconfig.node.json b/examples/cloud_provider/production_react_netlify/tsconfig.node.json similarity index 100% rename from tests_generated/test_all_default_parameters_jetbrains/tsconfig.node.json rename to examples/cloud_provider/production_react_netlify/tsconfig.node.json diff --git a/tests_generated/test_all_default_parameters_jetbrains/vite.config.ts b/examples/cloud_provider/production_react_netlify/vite.config.ts similarity index 100% rename from tests_generated/test_all_default_parameters_jetbrains/vite.config.ts rename to examples/cloud_provider/production_react_netlify/vite.config.ts diff --git a/examples/cloud_provider/production_react_vercel/.algokit.toml b/examples/cloud_provider/production_react_vercel/.algokit.toml new file mode 100644 index 0000000..f3abe6e --- /dev/null +++ b/examples/cloud_provider/production_react_vercel/.algokit.toml @@ -0,0 +1,10 @@ +[algokit] +min_version = "v1.3.0b1" + +[generate.import_contract] +description = "Import a typed client from your smart contracts project" +path = ".algokit/generators/import_contract" + +[project] +type = "frontend" +name = "production_react_vercel" diff --git a/examples/cloud_provider/production_react_vercel/.copier-answers.yml b/examples/cloud_provider/production_react_vercel/.copier-answers.yml new file mode 100644 index 0000000..1900174 --- /dev/null +++ b/examples/cloud_provider/production_react_vercel/.copier-answers.yml @@ -0,0 +1,9 @@ +# Changes here will be overwritten by Copier; NEVER EDIT MANUALLY +_commit: +_src_path: +author_email: None +author_name: None +cloud_provider: vercel +preset_name: production +project_name: production_react_vercel + diff --git a/tests_generated/test_all_default_parameters_on_vercel/.editorconfig b/examples/cloud_provider/production_react_vercel/.editorconfig similarity index 100% rename from tests_generated/test_all_default_parameters_on_vercel/.editorconfig rename to examples/cloud_provider/production_react_vercel/.editorconfig diff --git a/tests_generated/test_all_default_parameters_off/.env.template b/examples/cloud_provider/production_react_vercel/.env.template similarity index 100% rename from tests_generated/test_all_default_parameters_off/.env.template rename to examples/cloud_provider/production_react_vercel/.env.template diff --git a/tests_generated/test_all_default_parameters_on_vercel/.eslintrc b/examples/cloud_provider/production_react_vercel/.eslintrc similarity index 100% rename from tests_generated/test_all_default_parameters_on_vercel/.eslintrc rename to examples/cloud_provider/production_react_vercel/.eslintrc diff --git a/tests_generated/test_all_default_parameters_off/.gitattributes b/examples/cloud_provider/production_react_vercel/.gitattributes similarity index 100% rename from tests_generated/test_all_default_parameters_off/.gitattributes rename to examples/cloud_provider/production_react_vercel/.gitattributes diff --git a/examples/cloud_provider/production_react_vercel/.github/workflows/checks.yaml b/examples/cloud_provider/production_react_vercel/.github/workflows/checks.yaml new file mode 100644 index 0000000..4723b1a --- /dev/null +++ b/examples/cloud_provider/production_react_vercel/.github/workflows/checks.yaml @@ -0,0 +1,54 @@ +name: Check code base + +on: + workflow_call: + inputs: + run-build: + required: false + type: boolean + default: false + push: + branches: + - main + +jobs: + checks: + runs-on: 'ubuntu-latest' + steps: + - name: Check out repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup node + uses: actions/setup-node@v3 + with: + node-version: 18 + + - name: Install dependencies + run: npm ci + + - name: Run linters + run: npm run lint + + - name: Run unit tests + run: npm run test + + - name: Create placeholder .env file + if: ${{ inputs.run-build }} + uses: makerxstudio/shared-config/.github/actions/env-to-placeholders@main + with: + env-output-path: './.env' + env-template-path: './.env.template' + env-variable-prefix: VITE_ + + - name: Build + if: ${{ inputs.run-build }} + run: npm run build + + - name: Archive + if: ${{ inputs.run-build }} + uses: actions/upload-artifact@v4 + with: + name: dist + path: dist/ diff --git a/tests_generated/test_all_default_parameters_on_vercel/.github/workflows/pr.yaml b/examples/cloud_provider/production_react_vercel/.github/workflows/pr.yaml similarity index 100% rename from tests_generated/test_all_default_parameters_on_vercel/.github/workflows/pr.yaml rename to examples/cloud_provider/production_react_vercel/.github/workflows/pr.yaml diff --git a/tests_generated/test_all_default_parameters_on_vercel/.github/workflows/release.yaml b/examples/cloud_provider/production_react_vercel/.github/workflows/release.yaml similarity index 100% rename from tests_generated/test_all_default_parameters_on_vercel/.github/workflows/release.yaml rename to examples/cloud_provider/production_react_vercel/.github/workflows/release.yaml diff --git a/tests_generated/test_all_default_parameters_off/.gitignore b/examples/cloud_provider/production_react_vercel/.gitignore similarity index 100% rename from tests_generated/test_all_default_parameters_off/.gitignore rename to examples/cloud_provider/production_react_vercel/.gitignore diff --git a/examples/cloud_provider/production_react_vercel/.idea/runConfigurations/Run_Chrome.xml b/examples/cloud_provider/production_react_vercel/.idea/runConfigurations/Run_Chrome.xml new file mode 100644 index 0000000..9dfeeca --- /dev/null +++ b/examples/cloud_provider/production_react_vercel/.idea/runConfigurations/Run_Chrome.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/examples/cloud_provider/production_react_vercel/.idea/runConfigurations/Run_dApp.xml b/examples/cloud_provider/production_react_vercel/.idea/runConfigurations/Run_dApp.xml new file mode 100644 index 0000000..1cf2273 --- /dev/null +++ b/examples/cloud_provider/production_react_vercel/.idea/runConfigurations/Run_dApp.xml @@ -0,0 +1,12 @@ + + + + + + + + + diff --git a/examples/production_react/jest.config.ts b/examples/production_react/jest.config.ts new file mode 100644 index 0000000..28a8c3d --- /dev/null +++ b/examples/production_react/jest.config.ts @@ -0,0 +1,20 @@ +import type { Config } from '@jest/types' + +const config: Config.InitialOptions = { + preset: 'ts-jest', + testEnvironment: 'node', + testMatch: ['**/*.spec.ts', '**/*.spec.tsx'], + moduleDirectories: ['node_modules', 'src'], + transform: { + '': [ + 'ts-jest', + { + tsconfig: 'tsconfig.test.json', + }, + ], + }, + coveragePathIgnorePatterns: ['tests'], + testPathIgnorePatterns: ['/tests/'], + } + +export default config diff --git a/tests_generated/test_all_default_parameters_jetbrains/package.json b/examples/production_react/package.json similarity index 62% rename from tests_generated/test_all_default_parameters_jetbrains/package.json rename to examples/production_react/package.json index 62e7553..0cd594b 100644 --- a/tests_generated/test_all_default_parameters_jetbrains/package.json +++ b/examples/production_react/package.json @@ -1,5 +1,5 @@ { - "name": "test_all_default_parameters_jetbrains", + "name": "production_react", "version": "0.1.0", "author": { "name": "None", @@ -16,8 +16,19 @@ "@types/react-dom": "^18.2.4", "@vitejs/plugin-react": "^4.2.1", "autoprefixer": "^10.4.14", + "eslint": "^8.42.0", + "eslint-config-prettier": "^8.8.0", + "eslint-plugin-prettier": "^5.0.0", + "@typescript-eslint/eslint-plugin": "^6.5.0", + "@typescript-eslint/parser": "^6.5.0", + "postcss": "^8.4.24", + "tailwindcss": "3.3.2", + "ts-jest": "^29.1.1", + "@types/jest": "29.5.2", "ts-node": "^10.9.1", "typescript": "^5.1.6", + "@playwright/test": "^1.35.0", + "playwright": "^1.35.0", "vite": "^5.0.0" }, "dependencies": { @@ -28,6 +39,7 @@ "@perawallet/connect": "^1.3.1", "@txnlab/use-wallet": "^2.4.0", "algosdk": "^2.7.0", + "daisyui": "^4.0.0", "notistack": "^3.0.1", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -36,10 +48,15 @@ "scripts": { "dev": "vite", "build": "tsc && vite build", + "test": "jest --coverage --passWithNoTests", + "playwright:test": "playwright test", + "lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "lint:fix": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0 --fix", "preview": "vite preview" }, "eslintConfig": { "extends": [ + "react-app/jest", "react-app" ] }, diff --git a/examples/production_react/playwright.config.ts b/examples/production_react/playwright.config.ts new file mode 100644 index 0000000..d7cbca6 --- /dev/null +++ b/examples/production_react/playwright.config.ts @@ -0,0 +1,73 @@ +import { defineConfig, devices } from '@playwright/test' + +/** + * Read environment variables from file. + * https://github.com/motdotla/dotenv + */ +// require('dotenv').config(); + +/** + * See https://playwright.dev/docs/test-configuration. + */ +export default defineConfig({ + testDir: './tests', + /* Run tests in files in parallel */ + fullyParallel: true, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + /* Retry on CI only */ + retries: process.env.CI ? 2 : 0, + /* Opt out of parallel tests on CI. */ + workers: process.env.CI ? 1 : undefined, + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: 'html', + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Base URL to use in actions like `await page.goto('/')`. */ + // baseURL: 'http://127.0.0.1:3000', + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'on-first-retry', + testIdAttribute: 'data-test-id', + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + + { + name: 'firefox', + use: { ...devices['Desktop Firefox'] }, + }, + + /* Test against mobile viewports. */ + // { + // name: 'Mobile Chrome', + // use: { ...devices['Pixel 5'] }, + // }, + // { + // name: 'Mobile Safari', + // use: { ...devices['iPhone 12'] }, + // }, + + /* Test against branded browsers. */ + // { + // name: 'Microsoft Edge', + // use: { ...devices['Desktop Edge'], channel: 'msedge' }, + // }, + // { + // name: 'Google Chrome', + // use: { ..devices['Desktop Chrome'], channel: 'chrome' }, + // }, + ], + + /* Run your local dev server before starting the tests */ + webServer: { + command: 'npm run dev', + url: 'http://localhost:5173', + reuseExistingServer: !process.env.CI, + }, +}) diff --git a/examples/production_react/postcss.config.cjs b/examples/production_react/postcss.config.cjs new file mode 100644 index 0000000..33ad091 --- /dev/null +++ b/examples/production_react/postcss.config.cjs @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/examples/production_react/public/index.html b/examples/production_react/public/index.html new file mode 100644 index 0000000..0d3a3a5 --- /dev/null +++ b/examples/production_react/public/index.html @@ -0,0 +1,37 @@ + + + + + + + + + + React App + + + +
+ + + diff --git a/examples/production_react/public/robots.txt b/examples/production_react/public/robots.txt new file mode 100644 index 0000000..e9e57dc --- /dev/null +++ b/examples/production_react/public/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/examples/production_react/src/App.tsx b/examples/production_react/src/App.tsx new file mode 100644 index 0000000..58feddf --- /dev/null +++ b/examples/production_react/src/App.tsx @@ -0,0 +1,57 @@ +import { DeflyWalletConnect } from '@blockshake/defly-connect' +import { DaffiWalletConnect } from '@daffiwallet/connect' +import { PeraWalletConnect } from '@perawallet/connect' +import { PROVIDER_ID, ProvidersArray, WalletProvider, useInitializeProviders } from '@txnlab/use-wallet' +import algosdk from 'algosdk' +import { SnackbarProvider } from 'notistack' +import Home from './Home' +import { getAlgodConfigFromViteEnvironment, getKmdConfigFromViteEnvironment } from './utils/network/getAlgoClientConfigs' + +let providersArray: ProvidersArray +if (import.meta.env.VITE_ALGOD_NETWORK === '') { + const kmdConfig = getKmdConfigFromViteEnvironment() + providersArray = [ + { + id: PROVIDER_ID.KMD, + clientOptions: { + wallet: kmdConfig.wallet, + password: kmdConfig.password, + host: kmdConfig.server, + token: String(kmdConfig.token), + port: String(kmdConfig.port), + }, + }, + ] +} else { + providersArray = [ + { id: PROVIDER_ID.DEFLY, clientStatic: DeflyWalletConnect }, + { id: PROVIDER_ID.PERA, clientStatic: PeraWalletConnect }, + { id: PROVIDER_ID.DAFFI, clientStatic: DaffiWalletConnect }, + { id: PROVIDER_ID.EXODUS }, + // If you are interested in WalletConnect v2 provider + // refer to https://github.com/TxnLab/use-wallet for detailed integration instructions + ] +} + +export default function App() { + const algodConfig = getAlgodConfigFromViteEnvironment() + + const walletProviders = useInitializeProviders({ + providers: providersArray, + nodeConfig: { + network: algodConfig.network, + nodeServer: algodConfig.server, + nodePort: String(algodConfig.port), + nodeToken: String(algodConfig.token), + }, + algosdkStatic: algosdk, + }) + + return ( + + + + + + ) +} diff --git a/examples/production_react/src/Home.tsx b/examples/production_react/src/Home.tsx new file mode 100644 index 0000000..42128d8 --- /dev/null +++ b/examples/production_react/src/Home.tsx @@ -0,0 +1,63 @@ +// src/components/Home.tsx +import { useWallet } from '@txnlab/use-wallet' +import React, { useState } from 'react' +import ConnectWallet from './components/ConnectWallet' +import Transact from './components/Transact' + +interface HomeProps {} + +const Home: React.FC = () => { + const [openWalletModal, setOpenWalletModal] = useState(false) + const [openDemoModal, setOpenDemoModal] = useState(false) + const { activeAddress } = useWallet() + + const toggleWalletModal = () => { + setOpenWalletModal(!openWalletModal) + } + + const toggleDemoModal = () => { + setOpenDemoModal(!openDemoModal) + } + + return ( +
+
+
+

+ Welcome to
AlgoKit 🙂
+

+

+ This starter has been generated using official AlgoKit React template. Refer to the resource below for next steps. +

+ +
+ + Getting started + + +
+ + + {activeAddress && ( + + )} +
+ + + +
+
+
+ ) +} + +export default Home diff --git a/examples/production_react/src/assets/logo.svg b/examples/production_react/src/assets/logo.svg new file mode 100644 index 0000000..7169476 --- /dev/null +++ b/examples/production_react/src/assets/logo.svg @@ -0,0 +1 @@ + diff --git a/examples/production_react/src/components/Account.tsx b/examples/production_react/src/components/Account.tsx new file mode 100644 index 0000000..6a6345e --- /dev/null +++ b/examples/production_react/src/components/Account.tsx @@ -0,0 +1,28 @@ +import { useWallet } from '@txnlab/use-wallet' +import { useMemo } from 'react' +import { ellipseAddress } from '../utils/ellipseAddress' +import { getAlgodConfigFromViteEnvironment } from '../utils/network/getAlgoClientConfigs' + +const Account = () => { + const { activeAddress } = useWallet() + const algoConfig = getAlgodConfigFromViteEnvironment() + + const dappFlowNetworkName = useMemo(() => { + return algoConfig.network === '' ? 'sandbox' : algoConfig.network.toLocaleLowerCase() + }, [algoConfig.network]) + + return ( +
+ + Address: {ellipseAddress(activeAddress)} + +
Network: {algoConfig.network === '' ? 'localnet' : algoConfig.network}
+
+ ) +} + +export default Account diff --git a/examples/production_react/src/components/ConnectWallet.tsx b/examples/production_react/src/components/ConnectWallet.tsx new file mode 100644 index 0000000..c4225bc --- /dev/null +++ b/examples/production_react/src/components/ConnectWallet.tsx @@ -0,0 +1,86 @@ +import { Provider, useWallet } from '@txnlab/use-wallet' +import Account from './Account' + +interface ConnectWalletInterface { + openModal: boolean + closeModal: () => void +} + +const ConnectWallet = ({ openModal, closeModal }: ConnectWalletInterface) => { + const { providers, activeAddress } = useWallet() + + const isKmd = (provider: Provider) => provider.metadata.name.toLowerCase() === 'kmd' + + return ( + +
+

Select wallet provider

+ +
+ {activeAddress && ( + <> + +
+ + )} + + {!activeAddress && + providers?.map((provider) => ( + + ))} +
+ +
+ + {activeAddress && ( + + )} +
+ +
+ ) +} +export default ConnectWallet diff --git a/examples/production_react/src/components/ErrorBoundary.tsx b/examples/production_react/src/components/ErrorBoundary.tsx new file mode 100644 index 0000000..435bf61 --- /dev/null +++ b/examples/production_react/src/components/ErrorBoundary.tsx @@ -0,0 +1,46 @@ +import React, { ReactNode } from 'react' + +interface ErrorBoundaryProps { + children: ReactNode +} + +interface ErrorBoundaryState { + hasError: boolean + error: Error | null +} + +class ErrorBoundary extends React.Component { + constructor(props: ErrorBoundaryProps) { + super(props) + this.state = { hasError: false, error: null } + } + + static getDerivedStateFromError(error: Error): ErrorBoundaryState { + // Update state so the next render will show the fallback UI. + return { hasError: true, error: error } + } + + render(): ReactNode { + if (this.state.hasError) { + // You can render any custom fallback UI + return ( +
+
+
+

Error occured

+

+ {this.state.error?.message.includes('Attempt to get default algod configuration') + ? 'Please make sure to set up your environment variables correctly. Create a .env file based on .env.template and fill in the required values. This controls the network and credentials for connections with Algod and Indexer.' + : this.state.error?.message} +

+
+
+
+ ) + } + + return this.props.children + } +} + +export default ErrorBoundary diff --git a/examples/production_react/src/components/Transact.tsx b/examples/production_react/src/components/Transact.tsx new file mode 100644 index 0000000..16bd932 --- /dev/null +++ b/examples/production_react/src/components/Transact.tsx @@ -0,0 +1,95 @@ +import * as algokit from '@algorandfoundation/algokit-utils' +import { useWallet } from '@txnlab/use-wallet' +import algosdk from 'algosdk' +import { useSnackbar } from 'notistack' +import { useState } from 'react' +import { getAlgodConfigFromViteEnvironment } from '../utils/network/getAlgoClientConfigs' + +interface TransactInterface { + openModal: boolean + setModalState: (value: boolean) => void +} + +const Transact = ({ openModal, setModalState }: TransactInterface) => { + const [loading, setLoading] = useState(false) + const [receiverAddress, setReceiverAddress] = useState('') + + const algodConfig = getAlgodConfigFromViteEnvironment() + const algodClient = algokit.getAlgoClient({ + server: algodConfig.server, + port: algodConfig.port, + token: algodConfig.token, + }) + + const { enqueueSnackbar } = useSnackbar() + + const { signer, activeAddress, signTransactions, sendTransactions } = useWallet() + + const handleSubmitAlgo = async () => { + setLoading(true) + + if (!signer || !activeAddress) { + enqueueSnackbar('Please connect wallet first', { variant: 'warning' }) + return + } + + const suggestedParams = await algodClient.getTransactionParams().do() + + const transaction = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ + from: activeAddress, + to: receiverAddress, + amount: 1e6, + suggestedParams, + }) + + const encodedTransaction = algosdk.encodeUnsignedTransaction(transaction) + + const signedTransactions = await signTransactions([encodedTransaction]) + + const waitRoundsToConfirm = 4 + + try { + enqueueSnackbar('Sending transaction...', { variant: 'info' }) + const { id } = await sendTransactions(signedTransactions, waitRoundsToConfirm) + enqueueSnackbar(`Transaction sent: ${id}`, { variant: 'success' }) + setReceiverAddress('') + } catch (e) { + enqueueSnackbar('Failed to send transaction', { variant: 'error' }) + } + + setLoading(false) + } + + return ( + +
+

Send payment transaction

+
+ { + setReceiverAddress(e.target.value) + }} + /> +
+ + +
+
+
+ ) +} + +export default Transact diff --git a/examples/production_react/src/contracts/README.md b/examples/production_react/src/contracts/README.md new file mode 100644 index 0000000..e056b58 --- /dev/null +++ b/examples/production_react/src/contracts/README.md @@ -0,0 +1,13 @@ +## How to connect my web app with Algorand smart contracts? + +The following folder is reserved for the Algorand Application Clients. The clients are used to interact with instances of Algorand Smart Contracts (ASC1s) deployed on-chain. + +To integrate this react frontend template with your smart contracts codebase, perform the following steps: + +1. Generate the typed client using `algokit generate client -l typescript -o {path/to/this/folder}` +2. The generated typescript client should be ready to be imported and used in this react frontend template, making it a full fledged dApp. + +### FAQ + +- **How to interact with the smart contract?** + - The generated client provides a set of functions that can be used to interact with the ABI (Application Binary Interface) compliant Algorand smart contract. For example, if the smart contract has a function called `hello`, the generated client will have a function called `hello` that can be used to interact with the smart contract. Refer to a [full-stack end-to-end starter template](https://github.com/algorandfoundation/algokit-fullstack-template) for a reference example on invoking and interacting with typescript typed clients generated. diff --git a/examples/production_react/src/interfaces/network.ts b/examples/production_react/src/interfaces/network.ts new file mode 100644 index 0000000..a458edc --- /dev/null +++ b/examples/production_react/src/interfaces/network.ts @@ -0,0 +1,26 @@ +import { AlgoClientConfig } from '@algorandfoundation/algokit-utils/types/network-client' +import type { TokenHeader } from 'algosdk/dist/types/client/urlTokenBaseHTTPClient' + +export interface AlgoViteClientConfig extends AlgoClientConfig { + /** Base URL of the server e.g. http://localhost, https://testnet-api.algonode.cloud/, etc. */ + server: string + /** The port to use e.g. 4001, 443, etc. */ + port: string | number + /** The token to use for API authentication (or undefined if none needed) - can be a string, or an object with the header key => value */ + token: string | TokenHeader + /** String representing current Algorand Network type (testnet/mainnet and etc) */ + network: string +} + +export interface AlgoViteKMDConfig extends AlgoClientConfig { + /** Base URL of the server e.g. http://localhost, https://testnet-api.algonode.cloud/, etc. */ + server: string + /** The port to use e.g. 4001, 443, etc. */ + port: string | number + /** The token to use for API authentication (or undefined if none needed) - can be a string, or an object with the header key => value */ + token: string | TokenHeader + /** KMD wallet name */ + wallet: string + /** KMD wallet password */ + password: string +} diff --git a/examples/production_react/src/main.tsx b/examples/production_react/src/main.tsx new file mode 100644 index 0000000..adf72ec --- /dev/null +++ b/examples/production_react/src/main.tsx @@ -0,0 +1,13 @@ +import React from 'react' +import ReactDOM from 'react-dom/client' +import App from './App' +import './styles/main.css' +import ErrorBoundary from './components/ErrorBoundary' + +ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( + + + + + , +) diff --git a/examples/production_react/src/styles/main.css b/examples/production_react/src/styles/main.css new file mode 100644 index 0000000..b5c61c9 --- /dev/null +++ b/examples/production_react/src/styles/main.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; diff --git a/examples/production_react/src/utils/ellipseAddress.spec.tsx b/examples/production_react/src/utils/ellipseAddress.spec.tsx new file mode 100644 index 0000000..2cbff10 --- /dev/null +++ b/examples/production_react/src/utils/ellipseAddress.spec.tsx @@ -0,0 +1,15 @@ +import { ellipseAddress } from './ellipseAddress' + +describe('ellipseAddress', () => { + it('should return ellipsed address with specified width', () => { + const address = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' + const result = ellipseAddress(address, 4) + expect(result).toBe('aaaa...aaaa') + }) + + it('should return empty string when address is empty', () => { + const address = '' + const result = ellipseAddress(address) + expect(result).toBe('') + }) +}) diff --git a/examples/production_react/src/utils/ellipseAddress.ts b/examples/production_react/src/utils/ellipseAddress.ts new file mode 100644 index 0000000..542f46f --- /dev/null +++ b/examples/production_react/src/utils/ellipseAddress.ts @@ -0,0 +1,3 @@ +export function ellipseAddress(address = ``, width = 6): string { + return address ? `${address.slice(0, width)}...${address.slice(-width)}` : address +} diff --git a/examples/production_react/src/utils/network/getAlgoClientConfigs.ts b/examples/production_react/src/utils/network/getAlgoClientConfigs.ts new file mode 100644 index 0000000..b5121f8 --- /dev/null +++ b/examples/production_react/src/utils/network/getAlgoClientConfigs.ts @@ -0,0 +1,41 @@ +import { AlgoViteClientConfig, AlgoViteKMDConfig } from '../../interfaces/network' + +export function getAlgodConfigFromViteEnvironment(): AlgoViteClientConfig { + if (!import.meta.env.VITE_ALGOD_SERVER) { + throw new Error('Attempt to get default algod configuration without specifying VITE_ALGOD_SERVER in the environment variables') + } + + return { + server: import.meta.env.VITE_ALGOD_SERVER, + port: import.meta.env.VITE_ALGOD_PORT, + token: import.meta.env.VITE_ALGOD_TOKEN, + network: import.meta.env.VITE_ALGOD_NETWORK, + } +} + +export function getIndexerConfigFromViteEnvironment(): AlgoViteClientConfig { + if (!import.meta.env.VITE_INDEXER_SERVER) { + throw new Error('Attempt to get default algod configuration without specifying VITE_INDEXER_SERVER in the environment variables') + } + + return { + server: import.meta.env.VITE_INDEXER_SERVER, + port: import.meta.env.VITE_INDEXER_PORT, + token: import.meta.env.VITE_INDEXER_TOKEN, + network: import.meta.env.VITE_ALGOD_NETWORK, + } +} + +export function getKmdConfigFromViteEnvironment(): AlgoViteKMDConfig { + if (!import.meta.env.VITE_KMD_SERVER) { + throw new Error('Attempt to get default kmd configuration without specifying VITE_KMD_SERVER in the environment variables') + } + + return { + server: import.meta.env.VITE_KMD_SERVER, + port: import.meta.env.VITE_KMD_PORT, + token: import.meta.env.VITE_KMD_TOKEN, + wallet: import.meta.env.VITE_KMD_WALLET, + password: import.meta.env.VITE_KMD_PASSWORD, + } +} diff --git a/examples/production_react/src/vite-env.d.ts b/examples/production_react/src/vite-env.d.ts new file mode 100644 index 0000000..67c2d30 --- /dev/null +++ b/examples/production_react/src/vite-env.d.ts @@ -0,0 +1,24 @@ +/// + +interface ImportMetaEnv { + readonly VITE_ENVIRONMENT: string + + readonly VITE_ALGOD_TOKEN: string + readonly VITE_ALGOD_SERVER: string + readonly VITE_ALGOD_PORT: string + readonly VITE_ALGOD_NETWORK: string + + readonly VITE_INDEXER_TOKEN: string + readonly VITE_INDEXER_SERVER: string + readonly VITE_INDEXER_PORT: string + + readonly VITE_KMD_TOKEN: string + readonly VITE_KMD_SERVER: string + readonly VITE_KMD_PORT: string + readonly VITE_KMD_PASSWORD: string + readonly VITE_KMD_WALLET: string +} + +interface ImportMeta { + readonly env: ImportMetaEnv +} diff --git a/examples/production_react/tailwind.config.js b/examples/production_react/tailwind.config.js new file mode 100644 index 0000000..a9f7a95 --- /dev/null +++ b/examples/production_react/tailwind.config.js @@ -0,0 +1,11 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: ['./src/**/*.{js,ts,jsx,tsx}'], + theme: { + extend: {}, + }, + daisyui: { + themes: ['lofi'], + }, + plugins: [require('daisyui')], + } diff --git a/examples/production_react/tests/example.spec.ts b/examples/production_react/tests/example.spec.ts new file mode 100644 index 0000000..df83322 --- /dev/null +++ b/examples/production_react/tests/example.spec.ts @@ -0,0 +1,38 @@ +import { randomAccount } from '@algorandfoundation/algokit-utils' +import { expect, test } from '@playwright/test' + +test.beforeEach(async ({ page }) => { + await page.goto('http://localhost:5173/') +}) + +test('has title', async ({ page }) => { + // Expect a title "to contain" a substring. + await expect(page).toHaveTitle('AlgoKit React Template') +}) + +test('get started link', async ({ page }) => { + await expect(page.getByTestId('getting-started')).toHaveText('Getting started') +}) + +test('authentication and dummy payment transaction', async ({ page }) => { + page.on('dialog', async (dialog) => { + dialog.message() === 'KMD password' ? await dialog.accept() : await dialog.dismiss() + }) + + // 1. Must be able to connect to a KMD wallet provider + await page.getByTestId('connect-wallet').click() + await page.getByTestId('kmd-connect').click() + await page.getByTestId('close-wallet-modal').click() + + // 2. Must be able to send a dummy payment transaction + await page.getByTestId('transactions-demo').click() + + const dummyAccount = randomAccount() + await page.getByTestId('receiver-address').fill(dummyAccount.addr) + await page.getByTestId('send-algo').click() + + // 3. Must be able to see a notification that the transaction was sent + const notification = await page.getByText('Transaction sent:') + await notification.waitFor() + expect(notification).toBeTruthy() +}) diff --git a/examples/production_react/tsconfig.json b/examples/production_react/tsconfig.json new file mode 100644 index 0000000..5cde02a --- /dev/null +++ b/examples/production_react/tsconfig.json @@ -0,0 +1,38 @@ +{ + "compilerOptions": { + "target": "ES2020" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */, + "module": "ES2022" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, + "declaration": true /* Generates corresponding '.d.ts' file. */, + "declarationMap": true /* Generates a sourcemap for each corresponding '.d.ts' file. */, + "sourceMap": true /* Generates corresponding '.map' file. */, + "strict": true /* Enable all strict type-checking options. */, + "noImplicitReturns": true /* Report error when not all code paths in function return a value. */, + "noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */, + "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, + "experimentalDecorators": true /* Enables experimental support for ES7 decorators. */, + "emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */, + "skipLibCheck": true /* Skip type checking of declaration files. */, + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */, + "allowJs": false, + "allowSyntheticDefaultImports": true, + "moduleResolution": "Node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + "outDir": "./dist/" + }, + "include": [ + "src/**/*.ts", + "src/**/*.tsx", + "vite.config.js", + "src/utils/ellipseAddress.spec.tsx", + "src/utils/ellipseAddress.spec.tsx", + "src/main.tsx", + ], + "references": [ + { + "path": "./tsconfig.node.json" + } + ] +} diff --git a/examples/production_react/tsconfig.node.json b/examples/production_react/tsconfig.node.json new file mode 100644 index 0000000..9d31e2a --- /dev/null +++ b/examples/production_react/tsconfig.node.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "composite": true, + "module": "ESNext", + "moduleResolution": "Node", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/examples/production_react/vite.config.ts b/examples/production_react/vite.config.ts new file mode 100644 index 0000000..36f7f4e --- /dev/null +++ b/examples/production_react/vite.config.ts @@ -0,0 +1,7 @@ +import react from '@vitejs/plugin-react' +import { defineConfig } from 'vite' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], +}) diff --git a/examples/starter_react/.algokit.toml b/examples/starter_react/.algokit.toml new file mode 100644 index 0000000..58fbbe7 --- /dev/null +++ b/examples/starter_react/.algokit.toml @@ -0,0 +1,10 @@ +[algokit] +min_version = "v1.3.0b1" + +[generate.import_contract] +description = "Import a typed client from your smart contracts project" +path = ".algokit/generators/import_contract" + +[project] +type = "frontend" +name = "starter_react" diff --git a/examples/starter_react/.copier-answers.yml b/examples/starter_react/.copier-answers.yml new file mode 100644 index 0000000..e227958 --- /dev/null +++ b/examples/starter_react/.copier-answers.yml @@ -0,0 +1,8 @@ +# Changes here will be overwritten by Copier; NEVER EDIT MANUALLY +_commit: +_src_path: +author_email: None +author_name: None +preset_name: starter +project_name: starter_react + diff --git a/examples/starter_react/.env.template b/examples/starter_react/.env.template new file mode 100644 index 0000000..e05d499 --- /dev/null +++ b/examples/starter_react/.env.template @@ -0,0 +1,67 @@ +# ====================== +# LocalNet configuration +# uncomment below to use +# ====================== + +VITE_ENVIRONMENT=local + +# Algod +VITE_ALGOD_TOKEN=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +VITE_ALGOD_SERVER=http://localhost +VITE_ALGOD_PORT=4001 +VITE_ALGOD_NETWORK="" + +# Indexer +VITE_INDEXER_TOKEN=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +VITE_INDEXER_SERVER=http://localhost +VITE_INDEXER_PORT=8980 + +# KMD +# Please note: +# 1. This is only needed for LocalNet since +# by default KMD provider is ignored on other networks. +# 2. AlgoKit LocalNet starts with a single wallet called 'unencrypted-default-wallet', +# with heaps of tokens available for testing. +VITE_KMD_TOKEN=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +VITE_KMD_SERVER=http://localhost +VITE_KMD_PORT=4002 +VITE_KMD_WALLET="unencrypted-default-wallet" +VITE_KMD_PASSWORD="" + +# # ====================== +# # TestNet configuration: +# # uncomment below to use +# # ====================== + +# VITE_ENVIRONMENT=local + +# # Algod +# VITE_ALGOD_TOKEN="" +# VITE_ALGOD_SERVER="https://testnet-api.algonode.cloud" +# VITE_ALGOD_PORT="" +# VITE_ALGOD_NETWORK="testnet" + +# # Indexer +# VITE_INDEXER_TOKEN="" +# VITE_INDEXER_SERVER="https://testnet-idx.algonode.cloud" +# VITE_INDEXER_PORT="" + + +# # ====================== +# # MainNet configuration: +# # uncomment below to use +# # ====================== + +# VITE_ENVIRONMENT=production + +# # Algod +# VITE_ALGOD_TOKEN="" +# VITE_ALGOD_SERVER="https://mainnet-api.algonode.cloud" +# VITE_ALGOD_PORT="" +# VITE_ALGOD_NETWORK="mainnet" + +# # Indexer +# VITE_INDEXER_TOKEN="" +# VITE_INDEXER_SERVER="https://mainnet-idx.algonode.cloud" +# VITE_INDEXER_PORT="" + diff --git a/examples/starter_react/.gitattributes b/examples/starter_react/.gitattributes new file mode 100644 index 0000000..6313b56 --- /dev/null +++ b/examples/starter_react/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf diff --git a/examples/starter_react/.gitignore b/examples/starter_react/.gitignore new file mode 100644 index 0000000..26dc019 --- /dev/null +++ b/examples/starter_react/.gitignore @@ -0,0 +1,35 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# production +/build + + +# dotenv environment variable files +.env +env/ + +# misc +/dist +.DS_Store + + +npm-debug.log* +yarn-debug.log* +yarn-error.log* +/test-results/ +/playwright-report/ +/playwright/.cache/ + +# PyCharm +.idea +!.idea/ +.idea/* +!.idea/runConfigurations/ diff --git a/examples/starter_react/.vscode/extensions.json b/examples/starter_react/.vscode/extensions.json new file mode 100644 index 0000000..6d0554a --- /dev/null +++ b/examples/starter_react/.vscode/extensions.json @@ -0,0 +1,7 @@ +{ + "recommendations": [ + "EditorConfig.EditorConfig", + "dotenv.dotenv-vscode", + ] +} + diff --git a/examples/starter_react/.vscode/launch.json b/examples/starter_react/.vscode/launch.json new file mode 100644 index 0000000..7edaf04 --- /dev/null +++ b/examples/starter_react/.vscode/launch.json @@ -0,0 +1,68 @@ +{ + "configurations": [ + { + "type": "msedge", + "request": "launch", + "name": "Run (Edge)", + "url": "http://localhost:5173", + "webRoot": "${workspaceFolder}", + "presentation": { + "hidden": false, + "group": "2. Web" + } + }, + { + "type": "chrome", + "request": "launch", + "name": "Run (Chrome)", + "url": "http://localhost:5173", + "webRoot": "${workspaceFolder}", + "presentation": { + "hidden": false, + "group": "2. Web" + } + }, + { + "type": "firefox", + "request": "launch", + "name": "Run (Firefox)", + "url": "http://localhost:5173", + "webRoot": "${workspaceFolder}", + "presentation": { + "hidden": false, + "group": "2. Web" + } + }, + { + "name": "Run dApp", + "type": "node", + "request": "launch", + "runtimeExecutable": "npm", + "runtimeArgs": ["run", "dev"], + "cwd": "${workspaceRoot}", + "console": "integratedTerminal", + "skipFiles": ["/**", "node_modules/**"], + "presentation": { + "hidden": false, + "group": "1. Run Project", + "order": 1 + } + }, + { + "name": "Run dApp (+ LocalNet)", + "type": "node", + "request": "launch", + "runtimeExecutable": "npm", + "runtimeArgs": ["run", "dev"], + "cwd": "${workspaceRoot}", + "console": "integratedTerminal", + "skipFiles": ["/**", "node_modules/**"], + "preLaunchTask": "Start AlgoKit LocalNet", + "presentation": { + "hidden": false, + "group": "1. Run Project", + "order": 1 + } + } + ] +} diff --git a/examples/starter_react/.vscode/settings.json b/examples/starter_react/.vscode/settings.json new file mode 100644 index 0000000..f34d28b --- /dev/null +++ b/examples/starter_react/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "editor.formatOnSave": true, + + "dotenv.enableAutocloaking": false, + +} diff --git a/examples/starter_react/.vscode/tasks.json b/examples/starter_react/.vscode/tasks.json new file mode 100644 index 0000000..d611c4f --- /dev/null +++ b/examples/starter_react/.vscode/tasks.json @@ -0,0 +1,15 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Start AlgoKit LocalNet", + "command": "algokit", + "args": ["localnet", "start"], + "type": "shell", + "options": { + "cwd": "${workspaceFolder}" + }, + "problemMatcher": [] + } + ] +} diff --git a/examples/starter_react/LICENSE b/examples/starter_react/LICENSE new file mode 100644 index 0000000..b39bcd6 --- /dev/null +++ b/examples/starter_react/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Algorand Foundation + +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/tests_generated/test_all_default_parameters_jetbrains/README.md b/examples/starter_react/README.md similarity index 95% rename from tests_generated/test_all_default_parameters_jetbrains/README.md rename to examples/starter_react/README.md index 83235a8..ac13881 100644 --- a/tests_generated/test_all_default_parameters_jetbrains/README.md +++ b/examples/starter_react/README.md @@ -1,4 +1,4 @@ -# test_all_default_parameters_jetbrains +# starter_react This starter React project has been generated using AlgoKit. See below for default getting started instructions. @@ -52,6 +52,7 @@ This project makes use of React and Tailwind to provider a base project configur - [React](https://reactjs.org/) - A JavaScript library for building user interfaces. - [use-wallet](https://github.com/txnlab/use-wallet) - A React hook for connecting to an Algorand wallet providers. - [npm](https://www.npmjs.com/): Node.js package manager +It has also been configured to have a productive dev experience out of the box in [VS Code](https://code.visualstudio.com/), see the [.vscode](./.vscode) folder. # Integrating with smart contracts and application clients Refer to the detailed guidance on [integrating with smart contracts and application clients](./src/contracts/README.md). In essence, for any smart contract codebase generated with AlgoKit or other tools that produce compile contracts into ARC34 compliant app specifications, you can use the `algokit generate` command to generate TypeScript or Python typed client. Once generated simply drag and drop the generated client into `./src/contracts` and import it into your React components as you see fit. diff --git a/examples/starter_react/index.html b/examples/starter_react/index.html new file mode 100644 index 0000000..a85566a --- /dev/null +++ b/examples/starter_react/index.html @@ -0,0 +1,15 @@ + + + + + + AlgoKit React Template + + +
+ + + + diff --git a/tests_generated/test_all_default_parameters_off/package.json b/examples/starter_react/package.json similarity index 96% rename from tests_generated/test_all_default_parameters_off/package.json rename to examples/starter_react/package.json index 31d2a7d..dc25421 100644 --- a/tests_generated/test_all_default_parameters_off/package.json +++ b/examples/starter_react/package.json @@ -1,5 +1,5 @@ { - "name": "test_all_default_parameters_off", + "name": "starter_react", "version": "0.1.0", "author": { "name": "None", diff --git a/examples/starter_react/public/index.html b/examples/starter_react/public/index.html new file mode 100644 index 0000000..0d3a3a5 --- /dev/null +++ b/examples/starter_react/public/index.html @@ -0,0 +1,37 @@ + + + + + + + + + + React App + + + +
+ + + diff --git a/examples/starter_react/public/robots.txt b/examples/starter_react/public/robots.txt new file mode 100644 index 0000000..e9e57dc --- /dev/null +++ b/examples/starter_react/public/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/examples/starter_react/src/App.tsx b/examples/starter_react/src/App.tsx new file mode 100644 index 0000000..58feddf --- /dev/null +++ b/examples/starter_react/src/App.tsx @@ -0,0 +1,57 @@ +import { DeflyWalletConnect } from '@blockshake/defly-connect' +import { DaffiWalletConnect } from '@daffiwallet/connect' +import { PeraWalletConnect } from '@perawallet/connect' +import { PROVIDER_ID, ProvidersArray, WalletProvider, useInitializeProviders } from '@txnlab/use-wallet' +import algosdk from 'algosdk' +import { SnackbarProvider } from 'notistack' +import Home from './Home' +import { getAlgodConfigFromViteEnvironment, getKmdConfigFromViteEnvironment } from './utils/network/getAlgoClientConfigs' + +let providersArray: ProvidersArray +if (import.meta.env.VITE_ALGOD_NETWORK === '') { + const kmdConfig = getKmdConfigFromViteEnvironment() + providersArray = [ + { + id: PROVIDER_ID.KMD, + clientOptions: { + wallet: kmdConfig.wallet, + password: kmdConfig.password, + host: kmdConfig.server, + token: String(kmdConfig.token), + port: String(kmdConfig.port), + }, + }, + ] +} else { + providersArray = [ + { id: PROVIDER_ID.DEFLY, clientStatic: DeflyWalletConnect }, + { id: PROVIDER_ID.PERA, clientStatic: PeraWalletConnect }, + { id: PROVIDER_ID.DAFFI, clientStatic: DaffiWalletConnect }, + { id: PROVIDER_ID.EXODUS }, + // If you are interested in WalletConnect v2 provider + // refer to https://github.com/TxnLab/use-wallet for detailed integration instructions + ] +} + +export default function App() { + const algodConfig = getAlgodConfigFromViteEnvironment() + + const walletProviders = useInitializeProviders({ + providers: providersArray, + nodeConfig: { + network: algodConfig.network, + nodeServer: algodConfig.server, + nodePort: String(algodConfig.port), + nodeToken: String(algodConfig.token), + }, + algosdkStatic: algosdk, + }) + + return ( + + + + + + ) +} diff --git a/examples/starter_react/src/Home.tsx b/examples/starter_react/src/Home.tsx new file mode 100644 index 0000000..42128d8 --- /dev/null +++ b/examples/starter_react/src/Home.tsx @@ -0,0 +1,63 @@ +// src/components/Home.tsx +import { useWallet } from '@txnlab/use-wallet' +import React, { useState } from 'react' +import ConnectWallet from './components/ConnectWallet' +import Transact from './components/Transact' + +interface HomeProps {} + +const Home: React.FC = () => { + const [openWalletModal, setOpenWalletModal] = useState(false) + const [openDemoModal, setOpenDemoModal] = useState(false) + const { activeAddress } = useWallet() + + const toggleWalletModal = () => { + setOpenWalletModal(!openWalletModal) + } + + const toggleDemoModal = () => { + setOpenDemoModal(!openDemoModal) + } + + return ( +
+
+
+

+ Welcome to
AlgoKit 🙂
+

+

+ This starter has been generated using official AlgoKit React template. Refer to the resource below for next steps. +

+ +
+ + Getting started + + +
+ + + {activeAddress && ( + + )} +
+ + + +
+
+
+ ) +} + +export default Home diff --git a/examples/starter_react/src/assets/logo.svg b/examples/starter_react/src/assets/logo.svg new file mode 100644 index 0000000..7169476 --- /dev/null +++ b/examples/starter_react/src/assets/logo.svg @@ -0,0 +1 @@ + diff --git a/examples/starter_react/src/components/Account.tsx b/examples/starter_react/src/components/Account.tsx new file mode 100644 index 0000000..6a6345e --- /dev/null +++ b/examples/starter_react/src/components/Account.tsx @@ -0,0 +1,28 @@ +import { useWallet } from '@txnlab/use-wallet' +import { useMemo } from 'react' +import { ellipseAddress } from '../utils/ellipseAddress' +import { getAlgodConfigFromViteEnvironment } from '../utils/network/getAlgoClientConfigs' + +const Account = () => { + const { activeAddress } = useWallet() + const algoConfig = getAlgodConfigFromViteEnvironment() + + const dappFlowNetworkName = useMemo(() => { + return algoConfig.network === '' ? 'sandbox' : algoConfig.network.toLocaleLowerCase() + }, [algoConfig.network]) + + return ( +
+ + Address: {ellipseAddress(activeAddress)} + +
Network: {algoConfig.network === '' ? 'localnet' : algoConfig.network}
+
+ ) +} + +export default Account diff --git a/tests_generated/test_all_default_parameters_jetbrains/src/components/ConnectWallet.tsx b/examples/starter_react/src/components/ConnectWallet.tsx similarity index 100% rename from tests_generated/test_all_default_parameters_jetbrains/src/components/ConnectWallet.tsx rename to examples/starter_react/src/components/ConnectWallet.tsx diff --git a/examples/starter_react/src/components/ErrorBoundary.tsx b/examples/starter_react/src/components/ErrorBoundary.tsx new file mode 100644 index 0000000..435bf61 --- /dev/null +++ b/examples/starter_react/src/components/ErrorBoundary.tsx @@ -0,0 +1,46 @@ +import React, { ReactNode } from 'react' + +interface ErrorBoundaryProps { + children: ReactNode +} + +interface ErrorBoundaryState { + hasError: boolean + error: Error | null +} + +class ErrorBoundary extends React.Component { + constructor(props: ErrorBoundaryProps) { + super(props) + this.state = { hasError: false, error: null } + } + + static getDerivedStateFromError(error: Error): ErrorBoundaryState { + // Update state so the next render will show the fallback UI. + return { hasError: true, error: error } + } + + render(): ReactNode { + if (this.state.hasError) { + // You can render any custom fallback UI + return ( +
+
+
+

Error occured

+

+ {this.state.error?.message.includes('Attempt to get default algod configuration') + ? 'Please make sure to set up your environment variables correctly. Create a .env file based on .env.template and fill in the required values. This controls the network and credentials for connections with Algod and Indexer.' + : this.state.error?.message} +

+
+
+
+ ) + } + + return this.props.children + } +} + +export default ErrorBoundary diff --git a/tests_generated/test_all_default_parameters_jetbrains/src/components/Transact.tsx b/examples/starter_react/src/components/Transact.tsx similarity index 100% rename from tests_generated/test_all_default_parameters_jetbrains/src/components/Transact.tsx rename to examples/starter_react/src/components/Transact.tsx diff --git a/examples/starter_react/src/contracts/README.md b/examples/starter_react/src/contracts/README.md new file mode 100644 index 0000000..e056b58 --- /dev/null +++ b/examples/starter_react/src/contracts/README.md @@ -0,0 +1,13 @@ +## How to connect my web app with Algorand smart contracts? + +The following folder is reserved for the Algorand Application Clients. The clients are used to interact with instances of Algorand Smart Contracts (ASC1s) deployed on-chain. + +To integrate this react frontend template with your smart contracts codebase, perform the following steps: + +1. Generate the typed client using `algokit generate client -l typescript -o {path/to/this/folder}` +2. The generated typescript client should be ready to be imported and used in this react frontend template, making it a full fledged dApp. + +### FAQ + +- **How to interact with the smart contract?** + - The generated client provides a set of functions that can be used to interact with the ABI (Application Binary Interface) compliant Algorand smart contract. For example, if the smart contract has a function called `hello`, the generated client will have a function called `hello` that can be used to interact with the smart contract. Refer to a [full-stack end-to-end starter template](https://github.com/algorandfoundation/algokit-fullstack-template) for a reference example on invoking and interacting with typescript typed clients generated. diff --git a/examples/starter_react/src/interfaces/network.ts b/examples/starter_react/src/interfaces/network.ts new file mode 100644 index 0000000..a458edc --- /dev/null +++ b/examples/starter_react/src/interfaces/network.ts @@ -0,0 +1,26 @@ +import { AlgoClientConfig } from '@algorandfoundation/algokit-utils/types/network-client' +import type { TokenHeader } from 'algosdk/dist/types/client/urlTokenBaseHTTPClient' + +export interface AlgoViteClientConfig extends AlgoClientConfig { + /** Base URL of the server e.g. http://localhost, https://testnet-api.algonode.cloud/, etc. */ + server: string + /** The port to use e.g. 4001, 443, etc. */ + port: string | number + /** The token to use for API authentication (or undefined if none needed) - can be a string, or an object with the header key => value */ + token: string | TokenHeader + /** String representing current Algorand Network type (testnet/mainnet and etc) */ + network: string +} + +export interface AlgoViteKMDConfig extends AlgoClientConfig { + /** Base URL of the server e.g. http://localhost, https://testnet-api.algonode.cloud/, etc. */ + server: string + /** The port to use e.g. 4001, 443, etc. */ + port: string | number + /** The token to use for API authentication (or undefined if none needed) - can be a string, or an object with the header key => value */ + token: string | TokenHeader + /** KMD wallet name */ + wallet: string + /** KMD wallet password */ + password: string +} diff --git a/tests_generated/test_all_default_parameters_jetbrains/src/main.tsx b/examples/starter_react/src/main.tsx similarity index 100% rename from tests_generated/test_all_default_parameters_jetbrains/src/main.tsx rename to examples/starter_react/src/main.tsx diff --git a/tests_generated/test_all_default_parameters_jetbrains/src/styles/App.css b/examples/starter_react/src/styles/App.css similarity index 100% rename from tests_generated/test_all_default_parameters_jetbrains/src/styles/App.css rename to examples/starter_react/src/styles/App.css diff --git a/examples/starter_react/src/utils/ellipseAddress.ts b/examples/starter_react/src/utils/ellipseAddress.ts new file mode 100644 index 0000000..542f46f --- /dev/null +++ b/examples/starter_react/src/utils/ellipseAddress.ts @@ -0,0 +1,3 @@ +export function ellipseAddress(address = ``, width = 6): string { + return address ? `${address.slice(0, width)}...${address.slice(-width)}` : address +} diff --git a/examples/starter_react/src/utils/network/getAlgoClientConfigs.ts b/examples/starter_react/src/utils/network/getAlgoClientConfigs.ts new file mode 100644 index 0000000..b5121f8 --- /dev/null +++ b/examples/starter_react/src/utils/network/getAlgoClientConfigs.ts @@ -0,0 +1,41 @@ +import { AlgoViteClientConfig, AlgoViteKMDConfig } from '../../interfaces/network' + +export function getAlgodConfigFromViteEnvironment(): AlgoViteClientConfig { + if (!import.meta.env.VITE_ALGOD_SERVER) { + throw new Error('Attempt to get default algod configuration without specifying VITE_ALGOD_SERVER in the environment variables') + } + + return { + server: import.meta.env.VITE_ALGOD_SERVER, + port: import.meta.env.VITE_ALGOD_PORT, + token: import.meta.env.VITE_ALGOD_TOKEN, + network: import.meta.env.VITE_ALGOD_NETWORK, + } +} + +export function getIndexerConfigFromViteEnvironment(): AlgoViteClientConfig { + if (!import.meta.env.VITE_INDEXER_SERVER) { + throw new Error('Attempt to get default algod configuration without specifying VITE_INDEXER_SERVER in the environment variables') + } + + return { + server: import.meta.env.VITE_INDEXER_SERVER, + port: import.meta.env.VITE_INDEXER_PORT, + token: import.meta.env.VITE_INDEXER_TOKEN, + network: import.meta.env.VITE_ALGOD_NETWORK, + } +} + +export function getKmdConfigFromViteEnvironment(): AlgoViteKMDConfig { + if (!import.meta.env.VITE_KMD_SERVER) { + throw new Error('Attempt to get default kmd configuration without specifying VITE_KMD_SERVER in the environment variables') + } + + return { + server: import.meta.env.VITE_KMD_SERVER, + port: import.meta.env.VITE_KMD_PORT, + token: import.meta.env.VITE_KMD_TOKEN, + wallet: import.meta.env.VITE_KMD_WALLET, + password: import.meta.env.VITE_KMD_PASSWORD, + } +} diff --git a/examples/starter_react/src/vite-env.d.ts b/examples/starter_react/src/vite-env.d.ts new file mode 100644 index 0000000..67c2d30 --- /dev/null +++ b/examples/starter_react/src/vite-env.d.ts @@ -0,0 +1,24 @@ +/// + +interface ImportMetaEnv { + readonly VITE_ENVIRONMENT: string + + readonly VITE_ALGOD_TOKEN: string + readonly VITE_ALGOD_SERVER: string + readonly VITE_ALGOD_PORT: string + readonly VITE_ALGOD_NETWORK: string + + readonly VITE_INDEXER_TOKEN: string + readonly VITE_INDEXER_SERVER: string + readonly VITE_INDEXER_PORT: string + + readonly VITE_KMD_TOKEN: string + readonly VITE_KMD_SERVER: string + readonly VITE_KMD_PORT: string + readonly VITE_KMD_PASSWORD: string + readonly VITE_KMD_WALLET: string +} + +interface ImportMeta { + readonly env: ImportMetaEnv +} diff --git a/tests_generated/test_all_default_parameters_jetbrains/tsconfig.json b/examples/starter_react/tsconfig.json similarity index 100% rename from tests_generated/test_all_default_parameters_jetbrains/tsconfig.json rename to examples/starter_react/tsconfig.json diff --git a/examples/starter_react/tsconfig.node.json b/examples/starter_react/tsconfig.node.json new file mode 100644 index 0000000..9d31e2a --- /dev/null +++ b/examples/starter_react/tsconfig.node.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "composite": true, + "module": "ESNext", + "moduleResolution": "Node", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/examples/starter_react/vite.config.ts b/examples/starter_react/vite.config.ts new file mode 100644 index 0000000..36f7f4e --- /dev/null +++ b/examples/starter_react/vite.config.ts @@ -0,0 +1,7 @@ +import react from '@vitejs/plugin-react' +import { defineConfig } from 'vite' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], +}) diff --git a/poetry.lock b/poetry.lock index eae34f3..1ecebdb 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,112 +1,100 @@ -# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. [[package]] name = "aiohttp" -version = "3.8.6" +version = "3.9.3" description = "Async http client/server framework (asyncio)" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "aiohttp-3.8.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:41d55fc043954cddbbd82503d9cc3f4814a40bcef30b3569bc7b5e34130718c1"}, - {file = "aiohttp-3.8.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1d84166673694841d8953f0a8d0c90e1087739d24632fe86b1a08819168b4566"}, - {file = "aiohttp-3.8.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:253bf92b744b3170eb4c4ca2fa58f9c4b87aeb1df42f71d4e78815e6e8b73c9e"}, - {file = "aiohttp-3.8.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3fd194939b1f764d6bb05490987bfe104287bbf51b8d862261ccf66f48fb4096"}, - {file = "aiohttp-3.8.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6c5f938d199a6fdbdc10bbb9447496561c3a9a565b43be564648d81e1102ac22"}, - {file = "aiohttp-3.8.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2817b2f66ca82ee699acd90e05c95e79bbf1dc986abb62b61ec8aaf851e81c93"}, - {file = "aiohttp-3.8.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0fa375b3d34e71ccccf172cab401cd94a72de7a8cc01847a7b3386204093bb47"}, - {file = "aiohttp-3.8.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9de50a199b7710fa2904be5a4a9b51af587ab24c8e540a7243ab737b45844543"}, - {file = "aiohttp-3.8.6-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e1d8cb0b56b3587c5c01de3bf2f600f186da7e7b5f7353d1bf26a8ddca57f965"}, - {file = "aiohttp-3.8.6-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8e31e9db1bee8b4f407b77fd2507337a0a80665ad7b6c749d08df595d88f1cf5"}, - {file = "aiohttp-3.8.6-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:7bc88fc494b1f0311d67f29fee6fd636606f4697e8cc793a2d912ac5b19aa38d"}, - {file = "aiohttp-3.8.6-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ec00c3305788e04bf6d29d42e504560e159ccaf0be30c09203b468a6c1ccd3b2"}, - {file = "aiohttp-3.8.6-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ad1407db8f2f49329729564f71685557157bfa42b48f4b93e53721a16eb813ed"}, - {file = "aiohttp-3.8.6-cp310-cp310-win32.whl", hash = "sha256:ccc360e87341ad47c777f5723f68adbb52b37ab450c8bc3ca9ca1f3e849e5fe2"}, - {file = "aiohttp-3.8.6-cp310-cp310-win_amd64.whl", hash = "sha256:93c15c8e48e5e7b89d5cb4613479d144fda8344e2d886cf694fd36db4cc86865"}, - {file = "aiohttp-3.8.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e2f9cc8e5328f829f6e1fb74a0a3a939b14e67e80832975e01929e320386b34"}, - {file = "aiohttp-3.8.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e6a00ffcc173e765e200ceefb06399ba09c06db97f401f920513a10c803604ca"}, - {file = "aiohttp-3.8.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:41bdc2ba359032e36c0e9de5a3bd00d6fb7ea558a6ce6b70acedf0da86458321"}, - {file = "aiohttp-3.8.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14cd52ccf40006c7a6cd34a0f8663734e5363fd981807173faf3a017e202fec9"}, - {file = "aiohttp-3.8.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2d5b785c792802e7b275c420d84f3397668e9d49ab1cb52bd916b3b3ffcf09ad"}, - {file = "aiohttp-3.8.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1bed815f3dc3d915c5c1e556c397c8667826fbc1b935d95b0ad680787896a358"}, - {file = "aiohttp-3.8.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96603a562b546632441926cd1293cfcb5b69f0b4159e6077f7c7dbdfb686af4d"}, - {file = "aiohttp-3.8.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d76e8b13161a202d14c9584590c4df4d068c9567c99506497bdd67eaedf36403"}, - {file = "aiohttp-3.8.6-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e3f1e3f1a1751bb62b4a1b7f4e435afcdade6c17a4fd9b9d43607cebd242924a"}, - {file = "aiohttp-3.8.6-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:76b36b3124f0223903609944a3c8bf28a599b2cc0ce0be60b45211c8e9be97f8"}, - {file = "aiohttp-3.8.6-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:a2ece4af1f3c967a4390c284797ab595a9f1bc1130ef8b01828915a05a6ae684"}, - {file = "aiohttp-3.8.6-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:16d330b3b9db87c3883e565340d292638a878236418b23cc8b9b11a054aaa887"}, - {file = "aiohttp-3.8.6-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:42c89579f82e49db436b69c938ab3e1559e5a4409eb8639eb4143989bc390f2f"}, - {file = "aiohttp-3.8.6-cp311-cp311-win32.whl", hash = "sha256:efd2fcf7e7b9d7ab16e6b7d54205beded0a9c8566cb30f09c1abe42b4e22bdcb"}, - {file = "aiohttp-3.8.6-cp311-cp311-win_amd64.whl", hash = "sha256:3b2ab182fc28e7a81f6c70bfbd829045d9480063f5ab06f6e601a3eddbbd49a0"}, - {file = "aiohttp-3.8.6-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:fdee8405931b0615220e5ddf8cd7edd8592c606a8e4ca2a00704883c396e4479"}, - {file = "aiohttp-3.8.6-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d25036d161c4fe2225d1abff2bd52c34ed0b1099f02c208cd34d8c05729882f0"}, - {file = "aiohttp-3.8.6-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5d791245a894be071d5ab04bbb4850534261a7d4fd363b094a7b9963e8cdbd31"}, - {file = "aiohttp-3.8.6-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0cccd1de239afa866e4ce5c789b3032442f19c261c7d8a01183fd956b1935349"}, - {file = "aiohttp-3.8.6-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f13f60d78224f0dace220d8ab4ef1dbc37115eeeab8c06804fec11bec2bbd07"}, - {file = "aiohttp-3.8.6-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8a9b5a0606faca4f6cc0d338359d6fa137104c337f489cd135bb7fbdbccb1e39"}, - {file = "aiohttp-3.8.6-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:13da35c9ceb847732bf5c6c5781dcf4780e14392e5d3b3c689f6d22f8e15ae31"}, - {file = "aiohttp-3.8.6-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:4d4cbe4ffa9d05f46a28252efc5941e0462792930caa370a6efaf491f412bc66"}, - {file = "aiohttp-3.8.6-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:229852e147f44da0241954fc6cb910ba074e597f06789c867cb7fb0621e0ba7a"}, - {file = "aiohttp-3.8.6-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:713103a8bdde61d13490adf47171a1039fd880113981e55401a0f7b42c37d071"}, - {file = "aiohttp-3.8.6-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:45ad816b2c8e3b60b510f30dbd37fe74fd4a772248a52bb021f6fd65dff809b6"}, - {file = "aiohttp-3.8.6-cp36-cp36m-win32.whl", hash = "sha256:2b8d4e166e600dcfbff51919c7a3789ff6ca8b3ecce16e1d9c96d95dd569eb4c"}, - {file = "aiohttp-3.8.6-cp36-cp36m-win_amd64.whl", hash = "sha256:0912ed87fee967940aacc5306d3aa8ba3a459fcd12add0b407081fbefc931e53"}, - {file = "aiohttp-3.8.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e2a988a0c673c2e12084f5e6ba3392d76c75ddb8ebc6c7e9ead68248101cd446"}, - {file = "aiohttp-3.8.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebf3fd9f141700b510d4b190094db0ce37ac6361a6806c153c161dc6c041ccda"}, - {file = "aiohttp-3.8.6-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3161ce82ab85acd267c8f4b14aa226047a6bee1e4e6adb74b798bd42c6ae1f80"}, - {file = "aiohttp-3.8.6-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d95fc1bf33a9a81469aa760617b5971331cdd74370d1214f0b3109272c0e1e3c"}, - {file = "aiohttp-3.8.6-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c43ecfef7deaf0617cee936836518e7424ee12cb709883f2c9a1adda63cc460"}, - {file = "aiohttp-3.8.6-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca80e1b90a05a4f476547f904992ae81eda5c2c85c66ee4195bb8f9c5fb47f28"}, - {file = "aiohttp-3.8.6-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:90c72ebb7cb3a08a7f40061079817133f502a160561d0675b0a6adf231382c92"}, - {file = "aiohttp-3.8.6-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bb54c54510e47a8c7c8e63454a6acc817519337b2b78606c4e840871a3e15349"}, - {file = "aiohttp-3.8.6-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:de6a1c9f6803b90e20869e6b99c2c18cef5cc691363954c93cb9adeb26d9f3ae"}, - {file = "aiohttp-3.8.6-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:a3628b6c7b880b181a3ae0a0683698513874df63783fd89de99b7b7539e3e8a8"}, - {file = "aiohttp-3.8.6-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:fc37e9aef10a696a5a4474802930079ccfc14d9f9c10b4662169671ff034b7df"}, - {file = "aiohttp-3.8.6-cp37-cp37m-win32.whl", hash = "sha256:f8ef51e459eb2ad8e7a66c1d6440c808485840ad55ecc3cafefadea47d1b1ba2"}, - {file = "aiohttp-3.8.6-cp37-cp37m-win_amd64.whl", hash = "sha256:b2fe42e523be344124c6c8ef32a011444e869dc5f883c591ed87f84339de5976"}, - {file = "aiohttp-3.8.6-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:9e2ee0ac5a1f5c7dd3197de309adfb99ac4617ff02b0603fd1e65b07dc772e4b"}, - {file = "aiohttp-3.8.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:01770d8c04bd8db568abb636c1fdd4f7140b284b8b3e0b4584f070180c1e5c62"}, - {file = "aiohttp-3.8.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3c68330a59506254b556b99a91857428cab98b2f84061260a67865f7f52899f5"}, - {file = "aiohttp-3.8.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89341b2c19fb5eac30c341133ae2cc3544d40d9b1892749cdd25892bbc6ac951"}, - {file = "aiohttp-3.8.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:71783b0b6455ac8f34b5ec99d83e686892c50498d5d00b8e56d47f41b38fbe04"}, - {file = "aiohttp-3.8.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f628dbf3c91e12f4d6c8b3f092069567d8eb17814aebba3d7d60c149391aee3a"}, - {file = "aiohttp-3.8.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b04691bc6601ef47c88f0255043df6f570ada1a9ebef99c34bd0b72866c217ae"}, - {file = "aiohttp-3.8.6-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ee912f7e78287516df155f69da575a0ba33b02dd7c1d6614dbc9463f43066e3"}, - {file = "aiohttp-3.8.6-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9c19b26acdd08dd239e0d3669a3dddafd600902e37881f13fbd8a53943079dbc"}, - {file = "aiohttp-3.8.6-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:99c5ac4ad492b4a19fc132306cd57075c28446ec2ed970973bbf036bcda1bcc6"}, - {file = "aiohttp-3.8.6-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:f0f03211fd14a6a0aed2997d4b1c013d49fb7b50eeb9ffdf5e51f23cfe2c77fa"}, - {file = "aiohttp-3.8.6-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:8d399dade330c53b4106160f75f55407e9ae7505263ea86f2ccca6bfcbdb4921"}, - {file = "aiohttp-3.8.6-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ec4fd86658c6a8964d75426517dc01cbf840bbf32d055ce64a9e63a40fd7b771"}, - {file = "aiohttp-3.8.6-cp38-cp38-win32.whl", hash = "sha256:33164093be11fcef3ce2571a0dccd9041c9a93fa3bde86569d7b03120d276c6f"}, - {file = "aiohttp-3.8.6-cp38-cp38-win_amd64.whl", hash = "sha256:bdf70bfe5a1414ba9afb9d49f0c912dc524cf60141102f3a11143ba3d291870f"}, - {file = "aiohttp-3.8.6-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d52d5dc7c6682b720280f9d9db41d36ebe4791622c842e258c9206232251ab2b"}, - {file = "aiohttp-3.8.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4ac39027011414dbd3d87f7edb31680e1f430834c8cef029f11c66dad0670aa5"}, - {file = "aiohttp-3.8.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3f5c7ce535a1d2429a634310e308fb7d718905487257060e5d4598e29dc17f0b"}, - {file = "aiohttp-3.8.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b30e963f9e0d52c28f284d554a9469af073030030cef8693106d918b2ca92f54"}, - {file = "aiohttp-3.8.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:918810ef188f84152af6b938254911055a72e0f935b5fbc4c1a4ed0b0584aed1"}, - {file = "aiohttp-3.8.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:002f23e6ea8d3dd8d149e569fd580c999232b5fbc601c48d55398fbc2e582e8c"}, - {file = "aiohttp-3.8.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4fcf3eabd3fd1a5e6092d1242295fa37d0354b2eb2077e6eb670accad78e40e1"}, - {file = "aiohttp-3.8.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:255ba9d6d5ff1a382bb9a578cd563605aa69bec845680e21c44afc2670607a95"}, - {file = "aiohttp-3.8.6-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d67f8baed00870aa390ea2590798766256f31dc5ed3ecc737debb6e97e2ede78"}, - {file = "aiohttp-3.8.6-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:86f20cee0f0a317c76573b627b954c412ea766d6ada1a9fcf1b805763ae7feeb"}, - {file = "aiohttp-3.8.6-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:39a312d0e991690ccc1a61f1e9e42daa519dcc34ad03eb6f826d94c1190190dd"}, - {file = "aiohttp-3.8.6-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:e827d48cf802de06d9c935088c2924e3c7e7533377d66b6f31ed175c1620e05e"}, - {file = "aiohttp-3.8.6-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bd111d7fc5591ddf377a408ed9067045259ff2770f37e2d94e6478d0f3fc0c17"}, - {file = "aiohttp-3.8.6-cp39-cp39-win32.whl", hash = "sha256:caf486ac1e689dda3502567eb89ffe02876546599bbf915ec94b1fa424eeffd4"}, - {file = "aiohttp-3.8.6-cp39-cp39-win_amd64.whl", hash = "sha256:3f0e27e5b733803333bb2371249f41cf42bae8884863e8e8965ec69bebe53132"}, - {file = "aiohttp-3.8.6.tar.gz", hash = "sha256:b0cf2a4501bff9330a8a5248b4ce951851e415bdcce9dc158e76cfd55e15085c"}, + {file = "aiohttp-3.9.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:939677b61f9d72a4fa2a042a5eee2a99a24001a67c13da113b2e30396567db54"}, + {file = "aiohttp-3.9.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1f5cd333fcf7590a18334c90f8c9147c837a6ec8a178e88d90a9b96ea03194cc"}, + {file = "aiohttp-3.9.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:82e6aa28dd46374f72093eda8bcd142f7771ee1eb9d1e223ff0fa7177a96b4a5"}, + {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f56455b0c2c7cc3b0c584815264461d07b177f903a04481dfc33e08a89f0c26b"}, + {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bca77a198bb6e69795ef2f09a5f4c12758487f83f33d63acde5f0d4919815768"}, + {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e083c285857b78ee21a96ba1eb1b5339733c3563f72980728ca2b08b53826ca5"}, + {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab40e6251c3873d86ea9b30a1ac6d7478c09277b32e14745d0d3c6e76e3c7e29"}, + {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:df822ee7feaaeffb99c1a9e5e608800bd8eda6e5f18f5cfb0dc7eeb2eaa6bbec"}, + {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:acef0899fea7492145d2bbaaaec7b345c87753168589cc7faf0afec9afe9b747"}, + {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cd73265a9e5ea618014802ab01babf1940cecb90c9762d8b9e7d2cc1e1969ec6"}, + {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:a78ed8a53a1221393d9637c01870248a6f4ea5b214a59a92a36f18151739452c"}, + {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:6b0e029353361f1746bac2e4cc19b32f972ec03f0f943b390c4ab3371840aabf"}, + {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7cf5c9458e1e90e3c390c2639f1017a0379a99a94fdfad3a1fd966a2874bba52"}, + {file = "aiohttp-3.9.3-cp310-cp310-win32.whl", hash = "sha256:3e59c23c52765951b69ec45ddbbc9403a8761ee6f57253250c6e1536cacc758b"}, + {file = "aiohttp-3.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:055ce4f74b82551678291473f66dc9fb9048a50d8324278751926ff0ae7715e5"}, + {file = "aiohttp-3.9.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6b88f9386ff1ad91ace19d2a1c0225896e28815ee09fc6a8932fded8cda97c3d"}, + {file = "aiohttp-3.9.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c46956ed82961e31557b6857a5ca153c67e5476972e5f7190015018760938da2"}, + {file = "aiohttp-3.9.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:07b837ef0d2f252f96009e9b8435ec1fef68ef8b1461933253d318748ec1acdc"}, + {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad46e6f620574b3b4801c68255492e0159d1712271cc99d8bdf35f2043ec266"}, + {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ed3e046ea7b14938112ccd53d91c1539af3e6679b222f9469981e3dac7ba1ce"}, + {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:039df344b45ae0b34ac885ab5b53940b174530d4dd8a14ed8b0e2155b9dddccb"}, + {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7943c414d3a8d9235f5f15c22ace69787c140c80b718dcd57caaade95f7cd93b"}, + {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:84871a243359bb42c12728f04d181a389718710129b36b6aad0fc4655a7647d4"}, + {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5eafe2c065df5401ba06821b9a054d9cb2848867f3c59801b5d07a0be3a380ae"}, + {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:9d3c9b50f19704552f23b4eaea1fc082fdd82c63429a6506446cbd8737823da3"}, + {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:f033d80bc6283092613882dfe40419c6a6a1527e04fc69350e87a9df02bbc283"}, + {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:2c895a656dd7e061b2fd6bb77d971cc38f2afc277229ce7dd3552de8313a483e"}, + {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1f5a71d25cd8106eab05f8704cd9167b6e5187bcdf8f090a66c6d88b634802b4"}, + {file = "aiohttp-3.9.3-cp311-cp311-win32.whl", hash = "sha256:50fca156d718f8ced687a373f9e140c1bb765ca16e3d6f4fe116e3df7c05b2c5"}, + {file = "aiohttp-3.9.3-cp311-cp311-win_amd64.whl", hash = "sha256:5fe9ce6c09668063b8447f85d43b8d1c4e5d3d7e92c63173e6180b2ac5d46dd8"}, + {file = "aiohttp-3.9.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:38a19bc3b686ad55804ae931012f78f7a534cce165d089a2059f658f6c91fa60"}, + {file = "aiohttp-3.9.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:770d015888c2a598b377bd2f663adfd947d78c0124cfe7b959e1ef39f5b13869"}, + {file = "aiohttp-3.9.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ee43080e75fc92bf36219926c8e6de497f9b247301bbf88c5c7593d931426679"}, + {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52df73f14ed99cee84865b95a3d9e044f226320a87af208f068ecc33e0c35b96"}, + {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc9b311743a78043b26ffaeeb9715dc360335e5517832f5a8e339f8a43581e4d"}, + {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b955ed993491f1a5da7f92e98d5dad3c1e14dc175f74517c4e610b1f2456fb11"}, + {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:504b6981675ace64c28bf4a05a508af5cde526e36492c98916127f5a02354d53"}, + {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a6fe5571784af92b6bc2fda8d1925cccdf24642d49546d3144948a6a1ed58ca5"}, + {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ba39e9c8627edc56544c8628cc180d88605df3892beeb2b94c9bc857774848ca"}, + {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:e5e46b578c0e9db71d04c4b506a2121c0cb371dd89af17a0586ff6769d4c58c1"}, + {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:938a9653e1e0c592053f815f7028e41a3062e902095e5a7dc84617c87267ebd5"}, + {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:c3452ea726c76e92f3b9fae4b34a151981a9ec0a4847a627c43d71a15ac32aa6"}, + {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ff30218887e62209942f91ac1be902cc80cddb86bf00fbc6783b7a43b2bea26f"}, + {file = "aiohttp-3.9.3-cp312-cp312-win32.whl", hash = "sha256:38f307b41e0bea3294a9a2a87833191e4bcf89bb0365e83a8be3a58b31fb7f38"}, + {file = "aiohttp-3.9.3-cp312-cp312-win_amd64.whl", hash = "sha256:b791a3143681a520c0a17e26ae7465f1b6f99461a28019d1a2f425236e6eedb5"}, + {file = "aiohttp-3.9.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0ed621426d961df79aa3b963ac7af0d40392956ffa9be022024cd16297b30c8c"}, + {file = "aiohttp-3.9.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7f46acd6a194287b7e41e87957bfe2ad1ad88318d447caf5b090012f2c5bb528"}, + {file = "aiohttp-3.9.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:feeb18a801aacb098220e2c3eea59a512362eb408d4afd0c242044c33ad6d542"}, + {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f734e38fd8666f53da904c52a23ce517f1b07722118d750405af7e4123933511"}, + {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b40670ec7e2156d8e57f70aec34a7216407848dfe6c693ef131ddf6e76feb672"}, + {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fdd215b7b7fd4a53994f238d0f46b7ba4ac4c0adb12452beee724ddd0743ae5d"}, + {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:017a21b0df49039c8f46ca0971b3a7fdc1f56741ab1240cb90ca408049766168"}, + {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e99abf0bba688259a496f966211c49a514e65afa9b3073a1fcee08856e04425b"}, + {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:648056db9a9fa565d3fa851880f99f45e3f9a771dd3ff3bb0c048ea83fb28194"}, + {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8aacb477dc26797ee089721536a292a664846489c49d3ef9725f992449eda5a8"}, + {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:522a11c934ea660ff8953eda090dcd2154d367dec1ae3c540aff9f8a5c109ab4"}, + {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:5bce0dc147ca85caa5d33debc4f4d65e8e8b5c97c7f9f660f215fa74fc49a321"}, + {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4b4af9f25b49a7be47c0972139e59ec0e8285c371049df1a63b6ca81fdd216a2"}, + {file = "aiohttp-3.9.3-cp38-cp38-win32.whl", hash = "sha256:298abd678033b8571995650ccee753d9458dfa0377be4dba91e4491da3f2be63"}, + {file = "aiohttp-3.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:69361bfdca5468c0488d7017b9b1e5ce769d40b46a9f4a2eed26b78619e9396c"}, + {file = "aiohttp-3.9.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0fa43c32d1643f518491d9d3a730f85f5bbaedcbd7fbcae27435bb8b7a061b29"}, + {file = "aiohttp-3.9.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:835a55b7ca49468aaaac0b217092dfdff370e6c215c9224c52f30daaa735c1c1"}, + {file = "aiohttp-3.9.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:06a9b2c8837d9a94fae16c6223acc14b4dfdff216ab9b7202e07a9a09541168f"}, + {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abf151955990d23f84205286938796c55ff11bbfb4ccfada8c9c83ae6b3c89a3"}, + {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59c26c95975f26e662ca78fdf543d4eeaef70e533a672b4113dd888bd2423caa"}, + {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f95511dd5d0e05fd9728bac4096319f80615aaef4acbecb35a990afebe953b0e"}, + {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:595f105710293e76b9dc09f52e0dd896bd064a79346234b521f6b968ffdd8e58"}, + {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7c8b816c2b5af5c8a436df44ca08258fc1a13b449393a91484225fcb7545533"}, + {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f1088fa100bf46e7b398ffd9904f4808a0612e1d966b4aa43baa535d1b6341eb"}, + {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f59dfe57bb1ec82ac0698ebfcdb7bcd0e99c255bd637ff613760d5f33e7c81b3"}, + {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:361a1026c9dd4aba0109e4040e2aecf9884f5cfe1b1b1bd3d09419c205e2e53d"}, + {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:363afe77cfcbe3a36353d8ea133e904b108feea505aa4792dad6585a8192c55a"}, + {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e2c45c208c62e955e8256949eb225bd8b66a4c9b6865729a786f2aa79b72e9d"}, + {file = "aiohttp-3.9.3-cp39-cp39-win32.whl", hash = "sha256:f7217af2e14da0856e082e96ff637f14ae45c10a5714b63c77f26d8884cf1051"}, + {file = "aiohttp-3.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:27468897f628c627230dba07ec65dc8d0db566923c48f29e084ce382119802bc"}, + {file = "aiohttp-3.9.3.tar.gz", hash = "sha256:90842933e5d1ff760fae6caca4b2b3edba53ba8f4b71e95dacf2818a2aca06f7"}, ] [package.dependencies] aiosignal = ">=1.1.2" -async-timeout = ">=4.0.0a3,<5.0" +async-timeout = {version = ">=4.0,<5.0", markers = "python_version < \"3.11\""} attrs = ">=17.3.0" -charset-normalizer = ">=2.0,<4.0" frozenlist = ">=1.1.1" multidict = ">=4.5,<7.0" yarl = ">=1.0,<2.0" [package.extras] -speedups = ["Brotli", "aiodns", "cchardet"] +speedups = ["Brotli", "aiodns", "brotlicffi"] [[package]] name = "aiosignal" @@ -323,105 +311,6 @@ files = [ {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, ] -[[package]] -name = "charset-normalizer" -version = "3.3.1" -description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -optional = false -python-versions = ">=3.7.0" -files = [ - {file = "charset-normalizer-3.3.1.tar.gz", hash = "sha256:d9137a876020661972ca6eec0766d81aef8a5627df628b664b234b73396e727e"}, - {file = "charset_normalizer-3.3.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8aee051c89e13565c6bd366813c386939f8e928af93c29fda4af86d25b73d8f8"}, - {file = "charset_normalizer-3.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:352a88c3df0d1fa886562384b86f9a9e27563d4704ee0e9d56ec6fcd270ea690"}, - {file = "charset_normalizer-3.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:223b4d54561c01048f657fa6ce41461d5ad8ff128b9678cfe8b2ecd951e3f8a2"}, - {file = "charset_normalizer-3.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f861d94c2a450b974b86093c6c027888627b8082f1299dfd5a4bae8e2292821"}, - {file = "charset_normalizer-3.3.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1171ef1fc5ab4693c5d151ae0fdad7f7349920eabbaca6271f95969fa0756c2d"}, - {file = "charset_normalizer-3.3.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28f512b9a33235545fbbdac6a330a510b63be278a50071a336afc1b78781b147"}, - {file = "charset_normalizer-3.3.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0e842112fe3f1a4ffcf64b06dc4c61a88441c2f02f373367f7b4c1aa9be2ad5"}, - {file = "charset_normalizer-3.3.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3f9bc2ce123637a60ebe819f9fccc614da1bcc05798bbbaf2dd4ec91f3e08846"}, - {file = "charset_normalizer-3.3.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f194cce575e59ffe442c10a360182a986535fd90b57f7debfaa5c845c409ecc3"}, - {file = "charset_normalizer-3.3.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:9a74041ba0bfa9bc9b9bb2cd3238a6ab3b7618e759b41bd15b5f6ad958d17605"}, - {file = "charset_normalizer-3.3.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:b578cbe580e3b41ad17b1c428f382c814b32a6ce90f2d8e39e2e635d49e498d1"}, - {file = "charset_normalizer-3.3.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:6db3cfb9b4fcecb4390db154e75b49578c87a3b9979b40cdf90d7e4b945656e1"}, - {file = "charset_normalizer-3.3.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:debb633f3f7856f95ad957d9b9c781f8e2c6303ef21724ec94bea2ce2fcbd056"}, - {file = "charset_normalizer-3.3.1-cp310-cp310-win32.whl", hash = "sha256:87071618d3d8ec8b186d53cb6e66955ef2a0e4fa63ccd3709c0c90ac5a43520f"}, - {file = "charset_normalizer-3.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:e372d7dfd154009142631de2d316adad3cc1c36c32a38b16a4751ba78da2a397"}, - {file = "charset_normalizer-3.3.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ae4070f741f8d809075ef697877fd350ecf0b7c5837ed68738607ee0a2c572cf"}, - {file = "charset_normalizer-3.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:58e875eb7016fd014c0eea46c6fa92b87b62c0cb31b9feae25cbbe62c919f54d"}, - {file = "charset_normalizer-3.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dbd95e300367aa0827496fe75a1766d198d34385a58f97683fe6e07f89ca3e3c"}, - {file = "charset_normalizer-3.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de0b4caa1c8a21394e8ce971997614a17648f94e1cd0640fbd6b4d14cab13a72"}, - {file = "charset_normalizer-3.3.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:985c7965f62f6f32bf432e2681173db41336a9c2611693247069288bcb0c7f8b"}, - {file = "charset_normalizer-3.3.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a15c1fe6d26e83fd2e5972425a772cca158eae58b05d4a25a4e474c221053e2d"}, - {file = "charset_normalizer-3.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae55d592b02c4349525b6ed8f74c692509e5adffa842e582c0f861751701a673"}, - {file = "charset_normalizer-3.3.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be4d9c2770044a59715eb57c1144dedea7c5d5ae80c68fb9959515037cde2008"}, - {file = "charset_normalizer-3.3.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:851cf693fb3aaef71031237cd68699dded198657ec1e76a76eb8be58c03a5d1f"}, - {file = "charset_normalizer-3.3.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:31bbaba7218904d2eabecf4feec0d07469284e952a27400f23b6628439439fa7"}, - {file = "charset_normalizer-3.3.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:871d045d6ccc181fd863a3cd66ee8e395523ebfbc57f85f91f035f50cee8e3d4"}, - {file = "charset_normalizer-3.3.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:501adc5eb6cd5f40a6f77fbd90e5ab915c8fd6e8c614af2db5561e16c600d6f3"}, - {file = "charset_normalizer-3.3.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f5fb672c396d826ca16a022ac04c9dce74e00a1c344f6ad1a0fdc1ba1f332213"}, - {file = "charset_normalizer-3.3.1-cp311-cp311-win32.whl", hash = "sha256:bb06098d019766ca16fc915ecaa455c1f1cd594204e7f840cd6258237b5079a8"}, - {file = "charset_normalizer-3.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:8af5a8917b8af42295e86b64903156b4f110a30dca5f3b5aedea123fbd638bff"}, - {file = "charset_normalizer-3.3.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:7ae8e5142dcc7a49168f4055255dbcced01dc1714a90a21f87448dc8d90617d1"}, - {file = "charset_normalizer-3.3.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5b70bab78accbc672f50e878a5b73ca692f45f5b5e25c8066d748c09405e6a55"}, - {file = "charset_normalizer-3.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5ceca5876032362ae73b83347be8b5dbd2d1faf3358deb38c9c88776779b2e2f"}, - {file = "charset_normalizer-3.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34d95638ff3613849f473afc33f65c401a89f3b9528d0d213c7037c398a51296"}, - {file = "charset_normalizer-3.3.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9edbe6a5bf8b56a4a84533ba2b2f489d0046e755c29616ef8830f9e7d9cf5728"}, - {file = "charset_normalizer-3.3.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f6a02a3c7950cafaadcd46a226ad9e12fc9744652cc69f9e5534f98b47f3bbcf"}, - {file = "charset_normalizer-3.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10b8dd31e10f32410751b3430996f9807fc4d1587ca69772e2aa940a82ab571a"}, - {file = "charset_normalizer-3.3.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edc0202099ea1d82844316604e17d2b175044f9bcb6b398aab781eba957224bd"}, - {file = "charset_normalizer-3.3.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b891a2f68e09c5ef989007fac11476ed33c5c9994449a4e2c3386529d703dc8b"}, - {file = "charset_normalizer-3.3.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:71ef3b9be10070360f289aea4838c784f8b851be3ba58cf796262b57775c2f14"}, - {file = "charset_normalizer-3.3.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:55602981b2dbf8184c098bc10287e8c245e351cd4fdcad050bd7199d5a8bf514"}, - {file = "charset_normalizer-3.3.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:46fb9970aa5eeca547d7aa0de5d4b124a288b42eaefac677bde805013c95725c"}, - {file = "charset_normalizer-3.3.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:520b7a142d2524f999447b3a0cf95115df81c4f33003c51a6ab637cbda9d0bf4"}, - {file = "charset_normalizer-3.3.1-cp312-cp312-win32.whl", hash = "sha256:8ec8ef42c6cd5856a7613dcd1eaf21e5573b2185263d87d27c8edcae33b62a61"}, - {file = "charset_normalizer-3.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:baec8148d6b8bd5cee1ae138ba658c71f5b03e0d69d5907703e3e1df96db5e41"}, - {file = "charset_normalizer-3.3.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:63a6f59e2d01310f754c270e4a257426fe5a591dc487f1983b3bbe793cf6bac6"}, - {file = "charset_normalizer-3.3.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d6bfc32a68bc0933819cfdfe45f9abc3cae3877e1d90aac7259d57e6e0f85b1"}, - {file = "charset_normalizer-3.3.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4f3100d86dcd03c03f7e9c3fdb23d92e32abbca07e7c13ebd7ddfbcb06f5991f"}, - {file = "charset_normalizer-3.3.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39b70a6f88eebe239fa775190796d55a33cfb6d36b9ffdd37843f7c4c1b5dc67"}, - {file = "charset_normalizer-3.3.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e12f8ee80aa35e746230a2af83e81bd6b52daa92a8afaef4fea4a2ce9b9f4fa"}, - {file = "charset_normalizer-3.3.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b6cefa579e1237ce198619b76eaa148b71894fb0d6bcf9024460f9bf30fd228"}, - {file = "charset_normalizer-3.3.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:61f1e3fb621f5420523abb71f5771a204b33c21d31e7d9d86881b2cffe92c47c"}, - {file = "charset_normalizer-3.3.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4f6e2a839f83a6a76854d12dbebde50e4b1afa63e27761549d006fa53e9aa80e"}, - {file = "charset_normalizer-3.3.1-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:1ec937546cad86d0dce5396748bf392bb7b62a9eeb8c66efac60e947697f0e58"}, - {file = "charset_normalizer-3.3.1-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:82ca51ff0fc5b641a2d4e1cc8c5ff108699b7a56d7f3ad6f6da9dbb6f0145b48"}, - {file = "charset_normalizer-3.3.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:633968254f8d421e70f91c6ebe71ed0ab140220469cf87a9857e21c16687c034"}, - {file = "charset_normalizer-3.3.1-cp37-cp37m-win32.whl", hash = "sha256:c0c72d34e7de5604df0fde3644cc079feee5e55464967d10b24b1de268deceb9"}, - {file = "charset_normalizer-3.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:63accd11149c0f9a99e3bc095bbdb5a464862d77a7e309ad5938fbc8721235ae"}, - {file = "charset_normalizer-3.3.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5a3580a4fdc4ac05f9e53c57f965e3594b2f99796231380adb2baaab96e22761"}, - {file = "charset_normalizer-3.3.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2465aa50c9299d615d757c1c888bc6fef384b7c4aec81c05a0172b4400f98557"}, - {file = "charset_normalizer-3.3.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cb7cd68814308aade9d0c93c5bd2ade9f9441666f8ba5aa9c2d4b389cb5e2a45"}, - {file = "charset_normalizer-3.3.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91e43805ccafa0a91831f9cd5443aa34528c0c3f2cc48c4cb3d9a7721053874b"}, - {file = "charset_normalizer-3.3.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:854cc74367180beb327ab9d00f964f6d91da06450b0855cbbb09187bcdb02de5"}, - {file = "charset_normalizer-3.3.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c15070ebf11b8b7fd1bfff7217e9324963c82dbdf6182ff7050519e350e7ad9f"}, - {file = "charset_normalizer-3.3.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c4c99f98fc3a1835af8179dcc9013f93594d0670e2fa80c83aa36346ee763d2"}, - {file = "charset_normalizer-3.3.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3fb765362688821404ad6cf86772fc54993ec11577cd5a92ac44b4c2ba52155b"}, - {file = "charset_normalizer-3.3.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dced27917823df984fe0c80a5c4ad75cf58df0fbfae890bc08004cd3888922a2"}, - {file = "charset_normalizer-3.3.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a66bcdf19c1a523e41b8e9d53d0cedbfbac2e93c649a2e9502cb26c014d0980c"}, - {file = "charset_normalizer-3.3.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:ecd26be9f112c4f96718290c10f4caea6cc798459a3a76636b817a0ed7874e42"}, - {file = "charset_normalizer-3.3.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:3f70fd716855cd3b855316b226a1ac8bdb3caf4f7ea96edcccc6f484217c9597"}, - {file = "charset_normalizer-3.3.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:17a866d61259c7de1bdadef418a37755050ddb4b922df8b356503234fff7932c"}, - {file = "charset_normalizer-3.3.1-cp38-cp38-win32.whl", hash = "sha256:548eefad783ed787b38cb6f9a574bd8664468cc76d1538215d510a3cd41406cb"}, - {file = "charset_normalizer-3.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:45f053a0ece92c734d874861ffe6e3cc92150e32136dd59ab1fb070575189c97"}, - {file = "charset_normalizer-3.3.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bc791ec3fd0c4309a753f95bb6c749ef0d8ea3aea91f07ee1cf06b7b02118f2f"}, - {file = "charset_normalizer-3.3.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0c8c61fb505c7dad1d251c284e712d4e0372cef3b067f7ddf82a7fa82e1e9a93"}, - {file = "charset_normalizer-3.3.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2c092be3885a1b7899cd85ce24acedc1034199d6fca1483fa2c3a35c86e43041"}, - {file = "charset_normalizer-3.3.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c2000c54c395d9e5e44c99dc7c20a64dc371f777faf8bae4919ad3e99ce5253e"}, - {file = "charset_normalizer-3.3.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4cb50a0335382aac15c31b61d8531bc9bb657cfd848b1d7158009472189f3d62"}, - {file = "charset_normalizer-3.3.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c30187840d36d0ba2893bc3271a36a517a717f9fd383a98e2697ee890a37c273"}, - {file = "charset_normalizer-3.3.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe81b35c33772e56f4b6cf62cf4aedc1762ef7162a31e6ac7fe5e40d0149eb67"}, - {file = "charset_normalizer-3.3.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0bf89afcbcf4d1bb2652f6580e5e55a840fdf87384f6063c4a4f0c95e378656"}, - {file = "charset_normalizer-3.3.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:06cf46bdff72f58645434d467bf5228080801298fbba19fe268a01b4534467f5"}, - {file = "charset_normalizer-3.3.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:3c66df3f41abee950d6638adc7eac4730a306b022570f71dd0bd6ba53503ab57"}, - {file = "charset_normalizer-3.3.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:cd805513198304026bd379d1d516afbf6c3c13f4382134a2c526b8b854da1c2e"}, - {file = "charset_normalizer-3.3.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:9505dc359edb6a330efcd2be825fdb73ee3e628d9010597aa1aee5aa63442e97"}, - {file = "charset_normalizer-3.3.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:31445f38053476a0c4e6d12b047b08ced81e2c7c712e5a1ad97bc913256f91b2"}, - {file = "charset_normalizer-3.3.1-cp39-cp39-win32.whl", hash = "sha256:bd28b31730f0e982ace8663d108e01199098432a30a4c410d06fe08fdb9e93f4"}, - {file = "charset_normalizer-3.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:555fe186da0068d3354cdf4bbcbc609b0ecae4d04c921cc13e209eece7720727"}, - {file = "charset_normalizer-3.3.1-py3-none-any.whl", hash = "sha256:800561453acdecedaac137bf09cd719c7a440b6800ec182f077bb8e7025fb708"}, -] - [[package]] name = "click" version = "8.1.7" @@ -1223,6 +1112,7 @@ files = [ {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, @@ -1230,8 +1120,16 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, + {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, + {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, @@ -1248,6 +1146,7 @@ files = [ {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, @@ -1255,6 +1154,7 @@ files = [ {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, diff --git a/pyproject.toml b/pyproject.toml index 69d98f7..6c70e32 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,12 +8,12 @@ readme = "README.md" [tool.poetry.dependencies] python = "^3.10" beaker-pyteal = "^1.1.1" -algokit-utils = {version = "^2.1.0", allow-prereleases = true} +algokit-utils = { version = "^2.1.0", allow-prereleases = true } python-dotenv = "^1.0.0" [tool.poetry.group.dev.dependencies] ruff = ">=0.0.260" -black = {extras = ["d"], version = "^22.10.0"} +black = { extras = ["d"], version = "^22.10.0" } flake8 = "^6.0.0" pytest = "^7.2.2" mypy = "^1.1.1" @@ -26,8 +26,27 @@ requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" [tool.ruff] -select = ["E", "F", "ANN", "UP", "N", "C4", "B", "A", "YTT", "W", "FBT", "Q", "RUF", "I"] -extend-exclude = ["tests_generated/*python_linter-flake8", "tests_generated/*python_linter-none", "template_content"] +select = [ + "E", + "F", + "ANN", + "UP", + "N", + "C4", + "B", + "A", + "YTT", + "W", + "FBT", + "Q", + "RUF", + "I", +] +extend-exclude = [ + "examples/*python_linter-flake8", + "examples/*python_linter-none", + "template_content", +] ignore = [ "ANN101", # no type for self "ANN102", # no type for cls @@ -36,9 +55,7 @@ unfixable = ["B", "RUF"] [tool.pytest.ini_options] pythonpath = ["tests"] -testpaths = [ - "tests", -] +testpaths = ["tests"] [tool.mypy] python_version = "3.10" diff --git a/template_content/.algokit.toml b/template_content/.algokit.toml deleted file mode 100644 index 839cfe5..0000000 --- a/template_content/.algokit.toml +++ /dev/null @@ -1,2 +0,0 @@ -[algokit] -min_version = "v1.3.0b1" diff --git a/template_content/.algokit.toml.jinja b/template_content/.algokit.toml.jinja new file mode 100644 index 0000000..41f9604 --- /dev/null +++ b/template_content/.algokit.toml.jinja @@ -0,0 +1,10 @@ +[algokit] +min_version = "v1.3.0b1" + +[generate.import_contract] +description = "Import a typed client from your smart contracts project" +path = ".algokit/generators/import_contract" + +[project] +type = "frontend" +name = "{{ project_name }}" diff --git a/template_content/{% if use_github_actions %}.github{% endif %}/workflows/checks.yaml.jinja b/template_content/{% if use_github_actions %}.github{% endif %}/workflows/checks.yaml.jinja index 29fa82e..61aeddf 100644 --- a/template_content/{% if use_github_actions %}.github{% endif %}/workflows/checks.yaml.jinja +++ b/template_content/{% if use_github_actions %}.github{% endif %}/workflows/checks.yaml.jinja @@ -16,7 +16,7 @@ jobs: runs-on: 'ubuntu-latest' steps: - name: Check out repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 diff --git a/tests/test_custom_answers.py b/tests/test_custom_answers.py new file mode 100644 index 0000000..7f60ce6 --- /dev/null +++ b/tests/test_custom_answers.py @@ -0,0 +1,169 @@ +import re +import shutil +import subprocess +import tempfile +from collections.abc import Iterator +from pathlib import Path + +import pytest + +commit_pattern = re.compile(r"_commit: .*") +src_path_pattern = re.compile(r"_src_path: .*") +tests_path = Path(__file__).parent +root = tests_path.parent +generated_folder = "examples/cloud_provider" +# specific answer combination +generated_root = root / generated_folder + +config_path = Path(__file__).parent.parent / "pyproject.toml" +NPM_INSTALL_ARGS = ["npm", "install"] +NPM_LINT_ARGS = ["npm", "run", "lint"] +NPM_BUILD_ARGS = ["npm", "run", "build"] + + +def _generate_default_parameters( + *, preset_name: str, cloud_provider: str = "none" +) -> dict[str, str]: + return { + "author_name": "None", + "author_email": "None", + "preset_name": preset_name, + "cloud_provider": cloud_provider, + } + + +@pytest.fixture(autouse=True, scope="module") +def working_dir(custom_subdir: str = "template") -> Iterator[Path]: + with tempfile.TemporaryDirectory(ignore_cleanup_errors=True) as temp: + working_dir_path = Path(temp) / custom_subdir + working_generated_root = working_dir_path / generated_folder + shutil.copytree(root, working_dir_path) + subprocess.run(["git", "add", "-A"], cwd=working_dir_path) + subprocess.run( + ["git", "commit", "-m", "draft changes", "--no-verify"], + cwd=working_dir_path, + ) + + yield working_dir_path + + for src_dir in working_generated_root.iterdir(): + if not src_dir.is_dir(): + continue + + dest_dir = generated_root / src_dir.stem + shutil.rmtree(dest_dir, ignore_errors=True) + shutil.copytree(src_dir, dest_dir, dirs_exist_ok=True) + + +def run_init( + working_dir: Path, + test_name: str, + *args: str, + template_url: str | None = None, + template_branch: str | None = None, + answers: dict[str, str] | None = None, + custom_check_args: list[list[str]] | None = None, +) -> subprocess.CompletedProcess: + copy_to = working_dir / generated_folder / test_name + shutil.rmtree(copy_to, ignore_errors=True) + if template_url is None: + template_url = str(working_dir) + + if template_branch is None: + git_output = subprocess.run( + ["git", "rev-parse", "--abbrev-ref", "HEAD"], + cwd=working_dir, + stdout=subprocess.PIPE, + ) + template_branch = git_output.stdout.decode("utf-8").strip() + + init_args = [ + "algokit", + "--verbose", + "init", + "--name", + str(copy_to.stem), + "--template-url", + template_url, + "--UNSAFE-SECURITY-accept-template-url", + "--defaults", + "--no-ide", + "--no-git", + "--no-bootstrap", + "--no-workspace", + ] + answers = { + **(answers or {}), + } + + for question, answer in answers.items(): + init_args.extend(["-a", question, str(answer)]) + if template_branch: + init_args.extend(["--template-url-ref", template_branch]) + init_args.extend(args) + + result = subprocess.run( + init_args, + input="y", # acknowledge that input is not a tty + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, + cwd=copy_to.parent, + ) + + if result.returncode: + return result + # if successful, normalize .copier-answers.yml to make observing diffs easier + copier_answers = Path(copy_to / ".copier-answers.yml") + content = copier_answers.read_text("utf-8") + content = commit_pattern.sub("_commit: ", content) + content = src_path_pattern.sub("_src_path: ", content) + copier_answers.write_text(content, "utf-8") + + if custom_check_args: + for check_args in custom_check_args: + result = subprocess.run( + check_args, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, + cwd=copy_to, + ) + if result.returncode: + break + + # remove node_modules + shutil.rmtree(copy_to / "node_modules", ignore_errors=True) + + # Check if .idea folder exists and if so modify os specific SDK_HOME path + idea_folder = copy_to / ".idea" + if idea_folder.exists(): + # Iterate over all files in .idea/runConfigurations + for file in (idea_folder / "runConfigurations").iterdir(): + # Read the file content + content = file.read_text() + # Replace the line containing SDK_HOME + content = re.sub( + r'